diff --git a/.gitignore b/.gitignore
index 723ef36f4e4f32c4560383aa5987c575a30c6535..34142fe5c91654493d8dd95e68e432b6351f3196 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
-.idea
\ No newline at end of file
+.idea
+dist/
+release/
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 72a02bb52c2be573baee3f2c4f01e9c6680183a3..83f34df990ff1e4a1a128755bcfe548b02aa41ec 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,9 @@
-image: golang:latest
+image: golang:1.22
 
 stages:
   - test
+  - pre-release
+  - release
 
 run_tests:
   rules:
@@ -27,3 +29,125 @@ lint:
       codequality: gl-code-quality-report.json
     paths:
       - gl-code-quality-report.json
+
+# ----- prepare release -----
+
+# Расчет тега и формирование Changelog
+get_changelog:
+  stage: pre-release
+  image:
+    name: orhunp/git-cliff:latest
+    entrypoint: [ "" ]
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "pipeline" && $PREPARE_RELEASE == "true"' # Запуск при инициации из perxis
+    - if: $CI_PIPELINE_SOURCE == "web"                                      # или при ручном запуске из GUI
+      when: manual                    
+  variables:
+    GIT_STRATEGY: clone
+    GIT_DEPTH: 0
+  script:
+    - echo "$(git-cliff --unreleased | sed '1,6d' | sed '$d')" > current_changelog.md # удалить "лишние" строки для Changelog
+  artifacts:
+    reports:
+      dotenv: vars.env # Use artifacts:reports:dotenv to expose the variables to other jobs
+    paths:
+      - current_changelog.md
+
+# Релиз и запись тега в артефакт для использования в perxis
+prepare_release:
+  stage: pre-release
+  image: bitnami/git:latest
+  variables:
+    GIT_DEPTH: 0
+    GIT_STRATEGY: clone
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "pipeline" && $PREPARE_RELEASE == "true"'
+    - if: $CI_PIPELINE_SOURCE == "web"
+  needs:
+    - job: get_changelog
+      artifacts: true
+  script:
+    - |
+      CURRENT_VERSION=$(git describe --tags --abbrev=0)
+      RELEASE_VERSION=$CURRENT_VERSION
+      
+      # Релиз выполняется только при наличии изменений
+      if [ $(git rev-list $CURRENT_VERSION..HEAD --count) -gt 0 ]; then
+        # Если релиз на мастере, то устанавливается версия равная версии в Perxis
+        RELEASE_VERSION=$PERXIS_RELEASE_VERSION
+        
+        # Если релиз хотфиксовый, то увеличиваем патчевую версию
+        if [ $HOTFIX_RELEASE == "true" ]; then
+          RELEASE_VERSION=$(echo $CURRENT_VERSION | awk -F . '{print $1"."$2"."$3+1}')
+        fi
+      fi
+      
+      # Если новая версия совпадает со старой, значит изменений не было, и выпускать новый релиз не нужно
+      NEEDS_RELEASE=false
+      if [ "$RELEASE_VERSION" != "$CURRENT_VERSION" ]; then
+        NEEDS_RELEASE=true
+      fi
+        
+      echo "PERXIS_GO_CURRENT_VERSION=$CURRENT_VERSION" >> vars.env
+      echo "PERXIS_GO_RELEASE_VERSION=$RELEASE_VERSION" >> vars.env
+      echo "PERXIS_GO_NEEDS_RELEASE=$NEEDS_RELEASE" >> vars.env
+      
+      printf '### Release perxis-go
+      PERXIS_RELEASE_VERSION: %s
+      HOTFIX_RELEASE: %s
+      CURRENT_VERSION: %s
+      RELEASE_VERSION: %s
+      NEEDS_RELEASE: %s' $PERXIS_RELEASE_VERSION $HOTFIX_RELEASE $CURRENT_VERSION $RELEASE_VERSION $NEEDS_RELEASE
+  artifacts:
+    when: always
+    paths:
+      - vars.env
+      - current_changelog.md
+    expire_in: 1 week
+
+# ----- release -----
+
+create_release_branch:
+  stage: release
+  image: bitnami/git:latest
+  variables:
+    GIT_DEPTH: 0
+    GIT_STRATEGY: clone
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "pipeline" && $PREPARE_RELEASE != "true"
+    - if: $CI_PIPELINE_SOURCE == "web"
+  script:
+    - |
+      if [ $NEEDS_RELEASE == "true" ] && [ $CI_COMMIT_BRANCH == "master" ]; then
+        git config user.email "gitlab@ci-cd.pipeline"
+        git config user.name "gitlab-ci"
+        git remote remove gitlab_origin || true
+        git remote add gitlab_origin https://release_gitlab_ci:$PERXIS_GO_REPO_ACCESS_TOKEN@git.perx.ru/perxis/perxis-go.git
+        RELEASE_BRANCH=${VERSION:1:-2}
+        git branch release/$RELEASE_BRANCH || true
+        git push gitlab_origin release/$RELEASE_BRANCH
+      fi
+
+
+release:
+  stage: release
+  image: registry.gitlab.com/gitlab-org/release-cli:latest
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "pipeline" && $PREPARE_RELEASE != "true"
+    - if: $CI_PIPELINE_SOURCE == "web"
+      when: manual
+  needs:
+    - create_release_branch
+  script:
+    - echo "Start release $VERSION"
+    - |
+      if  [ $NEEDS_RELEASE != "true" ]; then
+        exit 203
+      fi
+  allow_failure:
+    exit_codes:
+      - 203
+  release:
+    name: 'Release $VERSION'
+    description: '$VERSION'
+    tag_name: '$VERSION'
diff --git a/.gitmodules b/.gitmodules
index 79c7c1ea054bf3ed90bc0be427d1350ee0519405..343aa6dbcfdb13e695b9bae0a198d12baa573973 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "perxis-proto"]
 	path = perxis-proto
 	url = https://git.perx.ru/perxis/perxis-proto.git
+
diff --git a/.goreleaser.yml b/.goreleaser.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9682c3a35ae139aceb327b9c367ef0779bd59998
--- /dev/null
+++ b/.goreleaser.yml
@@ -0,0 +1,25 @@
+version: 2
+
+gitlab_urls:
+  api: https://git.perx.ru/api/v4/
+  download: https://git.perx.ru
+  use_package_registry: true
+
+before:
+  hooks:
+    - go mod tidy
+
+builds:
+  - skip: true
+
+snapshot:
+  name_template: "{{ incpatch .Version }}-next"
+
+release:
+  gitlab:
+    owner: perxis
+    name: perxis-go
+
+announce:
+  skip: "{{gt .Patch 0}}"
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..a0685cbaeeed3790baa6a4d525a6658cf39cb83b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,145 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [0.28.0] - 2024-08-19
+
+### 🚀 Features
+
+- *(core)* Изменено поведение обхода схемы schema.Walk, считаем что объект изменился при установке значения Nil ([#PRXS-2648](https://tracker.yandex.ru/PRXS-2648))-([8a7f62f](https://git.perx.ru/perxis/perxis-go/-/commit/8a7f62fcd93f114e941538fe09f41b9c0927ceb0))
+- *(schema)* Реализован функционал для загрузки схем из файлов ([#PRXS-2731](https://tracker.yandex.ru/PRXS-2731))-([0255aad](https://git.perx.ru/perxis/perxis-go/-/commit/0255aad01984ac7b39385df1132f6ed3289e2ba4))
+- *(core)* Добавлен вызов Visitors при компиляции expr-выражения в bson. Добавлен метод Schema.SetSingleLocale ([#PRXS-2656](https://tracker.yandex.ru/PRXS-2656))-([60c45b0](https://git.perx.ru/perxis/perxis-go/-/commit/60c45b0cb14ae81835859ce9e5303a7fba5b733b))
+- Реализована загрузка данных схем и записей через файлы для использования расширениях -([a978cfa](https://git.perx.ru/perxis/perxis-go/-/commit/a978cfa2b8e69ef9b4be4b83d5a7c393f7521815))
+- *(core)* Добавлена функция для ожидания готовности окружения ([#PRXS-2648](https://tracker.yandex.ru/PRXS-2648))-([3e7c23e](https://git.perx.ru/perxis/perxis-go/-/commit/3e7c23efa6a7e42ddcdce460da22208234ab94bd))
+
+### 🐛 Bug Fixes
+
+- *(core)* Исправлена ошибка "assignment to entry in nil map" если у объекта схемы отсутствовали поля ([#PRXS-2770](https://tracker.yandex.ru/PRXS-2770))-([3c4b955](https://git.perx.ru/perxis/perxis-go/-/commit/3c4b955915e26b063aceac7169d6b0f82b7c96bf))
+- *(core)* Исправлена ошибка "panic: runtime error: invalid memory address or nil pointer dereference" при вызове UnmarshalJSON при передаче "null" ([#PRXS-2770](https://tracker.yandex.ru/PRXS-2770))-([36e8b6a](https://git.perx.ru/perxis/perxis-go/-/commit/36e8b6a965cd0b0a7977340c8eea5c9672f168a3))
+
+### ⚙️ Miscellaneous Tasks
+
+- Увеличен стандартный размер очереди Job Queue с 100 до 10000 -([ba53c5a](https://git.perx.ru/perxis/perxis-go/-/commit/ba53c5a0a0469e1ccdd18297ea38357342116a49))
+- *(schema)* Добавлено получение вложенных в Reference полей схемы -([7b14a23](https://git.perx.ru/perxis/perxis-go/-/commit/7b14a239c46a7700051a4115e274b55e0bee5e60))
+- GoReleaser configuration -([36ce38d](https://git.perx.ru/perxis/perxis-go/-/commit/36ce38dad5a17a3876dc9f26ea4b3ff09ed0793a))
+
+## [0.27.0] - 2024-07-24
+
+### 🚀 Features
+
+- Добавлен пакет `localizer` для  работы с переводами данных ([#PRXS-2653](https://tracker.yandex.ru/PRXS-2653))-([53a37c4](https://git.perx.ru/perxis/perxis-go/-/commit/53a37c45a8a76e2b4cf7f2b5a7874b06465269b9))
+- *(core)* Внесены изменения для работы с переводами через Items API -([ebf80bb](https://git.perx.ru/perxis/perxis-go/-/commit/ebf80bb317f126f85e69a813651054fbf77a2bae))
+- *(logs)* Обновление API логирования, релевантность результатов поиска ([#PRXS-2665](https://tracker.yandex.ru/PRXS-2665))-([97077fb](https://git.perx.ru/perxis/perxis-go/-/commit/97077fbc800a3c3d865ee35a485ed8b6396f3444))
+- *(core)* Обновление References API: поддержка локализации контента -([87b92c5](https://git.perx.ru/perxis/perxis-go/-/commit/87b92c5bc30d1c26b280c85b447a0ca9d313df34))
+- *(core)* Реализована возможность загрузки схем из YAML и JSON файлов ([#PRXS-2731](https://tracker.yandex.ru/PRXS-2731))-([bf29420](https://git.perx.ru/perxis/perxis-go/-/commit/bf294207182bbc0b5ce154029f98aaca00b9c831))
+
+### 🐛 Bug Fixes
+
+- *(core)* Исправлен обход схемы Walk для типа Array, решена ошибка отсутствия схемы при вызове Introspect -([2f684ea](https://git.perx.ru/perxis/perxis-go/-/commit/2f684ea678c0bcd59b8faed69cace00c9a4d29e6))
+- *(core)* Исправлена ошибка при которой не логировались действия с пространствами ([#PRXS-2609](https://tracker.yandex.ru/PRXS-2609))-([d0a7606](https://git.perx.ru/perxis/perxis-go/-/commit/d0a7606248f7e7a3a21075592d69d6845b546cce))
+- *(core)* Исправлена ошибка при которой была возможность изменить обьект находящийся в кеше Items ([#PRXS-2697](https://tracker.yandex.ru/PRXS-2697))-([cd5724d](https://git.perx.ru/perxis/perxis-go/-/commit/cd5724d316f2a0a6738a6159bdfb974938e8fde2))
+
+### ⚙️ Miscellaneous Tasks
+
+- *(api)* Исправлены ошибки linter`а связанные с пропущенными именами переменных при получении результатов ([#PRXS-2610](https://tracker.yandex.ru/PRXS-2610))-([90269af](https://git.perx.ru/perxis/perxis-go/-/commit/90269aff2bae12a05d5394eb223e08ff6c2bdb2f))
+- *(core)* Исправлена передача объектов в кэше сервисов ([#PRXS-2705](https://tracker.yandex.ru/PRXS-2705))-([9180ee7](https://git.perx.ru/perxis/perxis-go/-/commit/9180ee7aa4c433e02030659489adca91bc4c8401))
+
+## [0.25.1] - 2024-06-13
+
+### 🐛 Bug Fixes
+
+- *(core)* Исправлен deadlock при одновременном вызове BufferedWriteSyncer.Stop и синхронизации по интервалу ([#PRXS-2576](https://tracker.yandex.ru/PRXS-2576))-([3b823ed](https://git.perx.ru/perxis/perxis-go/-/commit/3b823ed00a22e142e25137dccb5f1c4fbb318d3f))
+
+## [0.25.0] - 2024-05-08
+
+### 🚀 Features
+
+- *(core)* Добавлена возможность использовать символ "-" в названиях полей ([#PRXS-1439](https://tracker.yandex.ru/PRXS-1439))-([66b02b9](https://git.perx.ru/perxis/perxis-go/-/commit/66b02b9a6defdbe5e5d5502ff45d67c38c2a5fa4))
+- *(api)* Добавлен метод внутренний метод установки состояния пространства SetState для сервиса Spaces ([#PRXS-2223](https://tracker.yandex.ru/PRXS-2223))-([0057019](https://git.perx.ru/perxis/perxis-go/-/commit/005701931ea9c03d8e54ffa6bbcf351254ff578e))
+- Добавлен метод Find для сервиса Spaces ([#PRXS-2298](https://tracker.yandex.ru/PRXS-2298))-([4135690](https://git.perx.ru/perxis/perxis-go/-/commit/41356904837085adc48f4c8c6edaf8ad7d7fd014))
+- *(core)* Добавлено системное поле SearchScore в Item ([#PRXS-2445](https://tracker.yandex.ru/PRXS-2445))-([087edb3](https://git.perx.ru/perxis/perxis-go/-/commit/087edb39bbe6d22e12dedd9d4795336556f7b844))
+
+### ⚙️ Miscellaneous Tasks
+
+- Перенесено содержимое пакета pkg/id на уровень выше в пакет id -([d13399f](https://git.perx.ru/perxis/perxis-go/-/commit/d13399fd6c26349f7ddf36c4d4e81568a2503a9c))
+- *(core)* Возврат GRPC-ошибки в сервисах -([c6bf265](https://git.perx.ru/perxis/perxis-go/-/commit/c6bf265699d5e9a99c0f583d7b6782b53cda3674))
+
+## [0.24.0] - 2024-04-12
+
+### 🚀 Features
+
+- Добавлен OID для Locales -([221d730](https://git.perx.ru/perxis/perxis-go/-/commit/221d730e552dd64d0dc32b21e799a75c6adf4503))
+- Добавлены методы Space, Environment, Collection, Organization для объекта System, который используется в шаблонах ([#PRXS-1813](https://tracker.yandex.ru/PRXS-1813))-([d017315](https://git.perx.ru/perxis/perxis-go/-/commit/d017315e664a9c5fcd84e377e5ba4f3635308f0b))
+
+### 🐛 Bug Fixes
+
+- *(core)* Изменено название и значение констант событий в logging_middleware для коллекций ([#PRXS-2199](https://tracker.yandex.ru/PRXS-2199))-([0cb42c5](https://git.perx.ru/perxis/perxis-go/-/commit/0cb42c57c0b976e9f9dd2f21e326f17d84580969))
+
+## [0.22.0] - 2024-03-06
+
+### 🚀 Features
+
+- В пакет data добавлена проверка при установке значения на то, инициализирована ли map -([978a2c9](https://git.perx.ru/perxis/perxis-go/-/commit/978a2c9dc0c6c554ed35f046e33321e2c250fcf2))
+- *(core)* Добавлены функции exists и len для языка запросов expr ([#PRXS-1883](https://tracker.yandex.ru/PRXS-1883))-([eae58bf](https://git.perx.ru/perxis/perxis-go/-/commit/eae58bf4c3cad9fe793b8ccf763bf9ada46266a3))
+- *(extensions)* Добавлена по умолчанию для отображения Icon первой колонкой для коллекции "Настройки/Действия" ([#PRXS-1950](https://tracker.yandex.ru/PRXS-1950))-([9a3936b](https://git.perx.ru/perxis/perxis-go/-/commit/9a3936bea15bd80201b1ae9ffe1518b424dcb1d1))
+- Add filter_core.go -([37d3135](https://git.perx.ru/perxis/perxis-go/-/commit/37d313519b69d83d97560a5c09722cec959596a9))
+- Add unused v2 entry encoder -([79d8eef](https://git.perx.ru/perxis/perxis-go/-/commit/79d8eefc88b4b7c26b855df1492506131184f1a9))
+
+### 🐛 Bug Fixes
+
+- Исправлена потенциальная утечка ресурсов в HTTP-загрузчике файлов (используется в сервисе Images) ([#PRXS-1977](https://tracker.yandex.ru/PRXS-1977))-([143766b](https://git.perx.ru/perxis/perxis-go/-/commit/143766b56f638a43281c26e9d79842198750db6b))
+- *(sync)* Исправлена ошибка синхронизации "received message larger than max". Процесс синхронизации значительно ускорен ([#PRXS-2026](https://tracker.yandex.ru/PRXS-2026))-([083aaf1](https://git.perx.ru/perxis/perxis-go/-/commit/083aaf1f363093219bb58f67b1accbc5a31f097a))
+- Add id import -([6c0ecb0](https://git.perx.ru/perxis/perxis-go/-/commit/6c0ecb0ee0786ae6f388e486c6210e79354f4f88))
+
+### ⚙️ Miscellaneous Tasks
+
+- Обновлена библиотека github.com/expr-lang/expr до версии v1.15.8 ([#PRXS-1667](https://tracker.yandex.ru/PRXS-1667))-([d0069ab](https://git.perx.ru/perxis/perxis-go/-/commit/d0069ab4893e7c0acf360b5a42759d74c6a5cb32))
+
+## [0.21.0] - 2024-01-16
+
+### 🚀 Features
+
+- Добавлен интерфейс Cache ([#PRXS-1859](https://tracker.yandex.ru/PRXS-1859))-([c11dae6](https://git.perx.ru/perxis/perxis-go/-/commit/c11dae6a20e9ff04d3840ba4de87998d9eb72e6e))
+- Внесены правки в Spaces после изменений в perxis-proto, перегенерированны grpc-клиенты для Spaces ([#PRXS-1835](https://tracker.yandex.ru/PRXS-1835))-([2a0c015](https://git.perx.ru/perxis/perxis-go/-/commit/2a0c01544486fac5f5cb505a1dab7aa88d7858f4))
+- Добавлена метрика для кэша -([3a6a7a3](https://git.perx.ru/perxis/perxis-go/-/commit/3a6a7a300dd7bad9597c4583871813ac5edecd52))
+- Функция argsToLabels была вынесена в пакет metrics и переименована в GetLabelsFromKV. Теперь функция игнорирует значение без ключа -([b0b4312](https://git.perx.ru/perxis/perxis-go/-/commit/b0b4312d8399b3933af35b88bad301a89488f353))
+- Добавлены metrics middleware для остальных сервисов -([8808371](https://git.perx.ru/perxis/perxis-go/-/commit/8808371cc465b0c16e6c41bd1ab5bf89a466a0ef))
+- *(core)* Добавлена сборка метрик кэша и запросов ([#PRXS-1219](https://tracker.yandex.ru/PRXS-1219))-([706b266](https://git.perx.ru/perxis/perxis-go/-/commit/706b2666190347d594c13d5d3f0f13586463be2a))
+- *(extension)* Добавлен middleware для сбора телеметрии -([221a13f](https://git.perx.ru/perxis/perxis-go/-/commit/221a13f8d19df03106afda566cad011178b1489a))
+- *(core)* Добавлен сбор телеметрии в менеджере расширений ([#PRXS-1898](https://tracker.yandex.ru/PRXS-1898))-([1f18b52](https://git.perx.ru/perxis/perxis-go/-/commit/1f18b5226a8473a5b509ce0cf7001f2e1525b395))
+
+### 🐛 Bug Fixes
+
+- *(items)* Исправлены тесты -([57e7765](https://git.perx.ru/perxis/perxis-go/-/commit/57e776563ae3f6b14338f7fbb2d073ad40496d51))
+- *(items)* Добавлена метрика в CachingMiddleware -([68796ee](https://git.perx.ru/perxis/perxis-go/-/commit/68796ee090f68e16092a88e480900807e229ba80))
+- *(delivery)* Добавлена метрика в CachingMiddleware -([3f182d1](https://git.perx.ru/perxis/perxis-go/-/commit/3f182d11c13c914f6b7b8556f4082ce03ce937cb))
+
+### 📚 Documentation
+
+- Добавлен комментарий -([c715ad8](https://git.perx.ru/perxis/perxis-go/-/commit/c715ad8d8123ca244f8eaa92f7456c9e99101fb4))
+- Добавлены подсказки к метрикам -([2387dac](https://git.perx.ru/perxis/perxis-go/-/commit/2387dac81918f252ac293a0f385d6e9149ec5924))
+
+## [0.20.0] - 2023-12-01
+
+### 🐛 Bug Fixes
+
+- *(extensions)* Исправлена ошибка, при которой при обновлении расширений для некоторых коллекций всегда переустанавливались схемы, в которых не было изменений. Добавлен возврат ошибки при любом действии с расширением в случае неправильной его конфигурации ([#PRXS-1695](https://tracker.yandex.ru/PRXS-1695))-([998f3e8](https://git.perx.ru/perxis/perxis-go/-/commit/998f3e84ba73b241740a404d097d2b7c65cd352a))
+- *(core)* Исправлена ошибка, при которой запрещенные правилами поля во вложенных объектах все равно были доступны пользователю ([#PRXS-1673](https://tracker.yandex.ru/PRXS-1673))-([0e1bec9](https://git.perx.ru/perxis/perxis-go/-/commit/0e1bec99fa78ad6f55e74e4cf6c38ee90cc5f205))
+
+## [0.19.0] - 2023-11-03
+
+### 🚀 Features
+
+- *(core)* Добавлено поле метадата в схему коллекций, для хранения дополнительной информации о коллекции -([05a7965](https://git.perx.ru/perxis/perxis-go/-/commit/05a7965aa95768f1053854947cf4f4365ce81fd7))
+- *(extensions)* Переработана процедура установки коллекций (Установка расширений) учитывающая что коллекция могла быть создана пользователем и предотвращающая перезапись и потерю данных ([#PRXS-1380](https://tracker.yandex.ru/PRXS-1380))-([1f00274](https://git.perx.ru/perxis/perxis-go/-/commit/1f00274400637dd64d83176f1b650c8b7c129f72))
+- *(extension)* При установке расширения схемы коллекций принадлежащих расширению по-умолчанию будут обновлены.  ([#PRXS-1380](https://tracker.yandex.ru/PRXS-1380))-([0dc7312](https://git.perx.ru/perxis/perxis-go/-/commit/0dc7312912999b811d5e48f247afbed3d737c9d7))
+- *(extensions)* При установке расширения больше не перезаписывают конфликтные схемы коллекций. (Удален временный флаг _alwaysSetSchema в Setup) ([#PRXS-1472](https://tracker.yandex.ru/PRXS-1472))-([278b2cb](https://git.perx.ru/perxis/perxis-go/-/commit/278b2cbc96e4ebfbf34e21dccffb929754f9a829))
+
+### 🐛 Bug Fixes
+
+- *(core)* Пакеты 'service' в которых лежат middlewares переименованы на 'middleware' ([#PRXS-1163](https://tracker.yandex.ru/PRXS-1163))-([75f3153](https://git.perx.ru/perxis/perxis-go/-/commit/75f31532aa7b3007d9ff32c89ec6b62877b93085))
+- *(core)* Исправлена ошибка, из-за которой проверка на принадлежность пользователя к разным организациям отрабатывала некорректно. Добавлена очистка кэша при переносе пространства ([#PRXS-1297](https://tracker.yandex.ru/PRXS-1297))-([7aa1d49](https://git.perx.ru/perxis/perxis-go/-/commit/7aa1d497cbd84e4e7b08508d645d794ef77a27ac))
+- *(sdk)* Исправлена ошибка 'failed to uninstall client: not found', возникающая при удалении расширения, клиент которого уже не существует ([#PRXS-1342](https://tracker.yandex.ru/PRXS-1342))-([a170724](https://git.perx.ru/perxis/perxis-go/-/commit/a1707249ff2b82589a990cf08352debb58d467df))
+- *(core)* Исправлена выгрузка лишних полей ("published_at", "published_by", "archived_at","archived_by")  в файл при экспорте данных ([#PRXS-1390](https://tracker.yandex.ru/PRXS-1390))-([6dc87f8](https://git.perx.ru/perxis/perxis-go/-/commit/6dc87f8bf1acf2e12a7dc9858ab6407b9ac9ee78))
+
+<!-- generated by git-cliff -->
diff --git a/Taskfile.yaml b/Taskfile.yaml
index 8a16d687681a42527f9dca01dd01fef0790c0aca..a75ab1d82db97c1725b2de039d57ab0e8d20b882 100644
--- a/Taskfile.yaml
+++ b/Taskfile.yaml
@@ -1,11 +1,31 @@
 version: '3'
 
-
 vars:
   PROTODIR: perxis-proto/proto
   PBDIR: pb
+  CURRENT_VERSION:
+    sh: svu current
+  RELEASE_VERSION:
+    sh: svu next
 
 tasks:
+  changelog:
+    cmds:
+      - git-cliff > CHANGELOG.md --tag {{ .RELEASE_VERSION }}
+
+# release
+# - Сделать changelog
+# - Закоммитить все изменения
+# - Пометить тэгом версию
+#   пререлиз - `git tag "$(svu pr --pre-release alpha.1 --build 9)"`
+#   пререлиз - `git tag "$(svu next)"`
+# - Запушить код и тэги на сервер (иначе будет непонятная ошибка goreleaser Not found)
+  release:
+    cmds:
+      - mkdir -p release
+      - git-cliff {{ .CURRENT_VERSION }}.. --tag {{ .RELEASE_VERSION }}  > release/CHANGELOG.md
+      - goreleaser release --clean --release-notes=release/CHANGELOG.md
+
   mocks:
     deps:
       - mocks.proto
diff --git a/assets.go b/assets.go
new file mode 100644
index 0000000000000000000000000000000000000000..b3ed90c1011a144565e9fc8f293473352ba6cba3
--- /dev/null
+++ b/assets.go
@@ -0,0 +1,168 @@
+package perxis
+
+import (
+	"io"
+	"io/fs"
+	"path/filepath"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	jsoniter "github.com/json-iterator/go"
+	"gopkg.in/yaml.v3"
+)
+
+// Assets предоставляет методы для загрузки данных из файловой системы
+type Assets[T any] struct {
+	Constructor func() T
+}
+
+// NewAssets возвращает новый экземпляр загрузчика
+func NewAssets[T any]() *Assets[T] {
+	return &Assets[T]{
+		Constructor: func() (t T) { return t },
+	}
+}
+
+type FromFSFunc[T any] func(fsys fs.FS) ([]T, error)
+type FromFileFunc[T any] func(file fs.File) ([]T, error)
+
+func (a *Assets[T]) Funcs() (FromFSFunc[T], FromFileFunc[T]) {
+	return a.FromFS, a.FromFile
+}
+
+// WithConstructor устанавливает конструктор для создания новых экземпляров
+func (a *Assets[T]) WithConstructor(t func() T) *Assets[T] {
+	a.Constructor = t
+	return a
+}
+
+// MustFrom возвращает все записи в переданной файловой системе
+func (a *Assets[T]) MustFrom(fsys fs.FS, path string) []T {
+	res, err := a.From(fsys, path)
+	if err != nil {
+		panic(err)
+	}
+	return res
+}
+
+// MustOneFrom возвращает одну запись из переданного файла
+func (a *Assets[T]) MustOneFrom(fsys fs.FS, path string) T {
+	res, err := a.From(fsys, path)
+	if err != nil {
+		panic(err)
+	}
+	if len(res) == 0 {
+		panic(errors.Errorf("no entries found"))
+	}
+	if len(res) > 1 {
+		panic(errors.Errorf("multiple entries found"))
+	}
+	return res[0]
+}
+
+// From возвращает записи из переданного файла
+func (a *Assets[T]) From(fsys fs.FS, path string) ([]T, error) {
+	f, err := fsys.Open(path)
+	if err != nil {
+		return nil, err
+	}
+
+	defer f.Close()
+
+	stat, err := f.Stat()
+	if err != nil {
+		return nil, err
+	}
+
+	if stat.IsDir() {
+		sub, err := fs.Sub(fsys, path)
+		if err != nil {
+			return nil, err
+		}
+		return a.FromFS(sub)
+	}
+
+	return a.FromFile(f)
+}
+
+// FromFile возвращает записи в переданном файле
+func (a *Assets[T]) FromFile(file fs.File) ([]T, error) {
+	stat, err := file.Stat()
+	if err != nil {
+		return nil, err
+	}
+
+	switch filepath.Ext(stat.Name()) {
+	case ".json":
+		entry, err := a.FromJSON(file)
+		if err != nil {
+			return nil, errors.Wrapf(err, "file '%s'", stat.Name())
+		}
+		return []T{entry}, nil
+
+	case ".yaml", ".yml":
+		entries, err := a.FromYAML(file)
+		return entries, errors.Wrapf(err, "file '%s'", stat.Name())
+	}
+
+	return nil, errors.Errorf("file '%s' must be in JSON or YAML format", stat.Name())
+}
+
+// FromFS возвращает все записи в переданной файловой системе
+func (a *Assets[T]) FromFS(fsys fs.FS) (result []T, err error) {
+	if err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, _ error) error {
+		file, err := fsys.Open(path)
+		if err != nil {
+			return err
+		}
+		defer file.Close()
+
+		if entries, err := a.FromFile(file); err == nil {
+			result = append(result, entries...)
+		}
+		return nil
+	}); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+// FromJSON возвращает запись из JSON
+func (c *Assets[T]) FromJSON(r io.Reader) (T, error) {
+	entry := c.Constructor()
+	data, err := io.ReadAll(r)
+	if err != nil {
+		return entry, err
+	}
+
+	err = jsoniter.Unmarshal(data, &entry)
+	return entry, err
+}
+
+// FromYAML возвращает записи из YAML
+func (c *Assets[T]) FromYAML(r io.Reader) (result []T, err error) {
+	decoder := yaml.NewDecoder(r)
+	for {
+		var data interface{}
+		err = decoder.Decode(&data)
+		if errors.Is(err, io.EOF) {
+			break
+		}
+		if err != nil {
+			return nil, err
+		}
+
+		json, err := jsoniter.Marshal(data)
+		if err != nil {
+			return nil, err
+		}
+
+		entry := c.Constructor()
+		if err = jsoniter.Unmarshal(json, &entry); err != nil {
+			return nil, err
+		}
+		result = append(result, entry)
+	}
+
+	return result, nil
+}
diff --git a/assets/tests/assets/invalid.txt b/assets/tests/assets/invalid.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/assets/tests/assets/item.json b/assets/tests/assets/item.json
new file mode 100644
index 0000000000000000000000000000000000000000..1240b61e1efb8370c3000cafd213a6dfe8de7d53
--- /dev/null
+++ b/assets/tests/assets/item.json
@@ -0,0 +1,16 @@
+{
+  "id": "item3",
+  "enum": 1,
+  "data": {
+    "obj": {
+      "str": "value"
+    },
+    "arr": [
+      "str1",
+      "str2"
+    ]
+  },
+  "struct": {
+    "option": true
+  }
+}
\ No newline at end of file
diff --git a/assets/tests/assets/items.yaml b/assets/tests/assets/items.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..eacf244f47504be87023f538cdd7f02cdbbe7ea0
--- /dev/null
+++ b/assets/tests/assets/items.yaml
@@ -0,0 +1,23 @@
+---
+id: item1
+enum: 1
+data:
+  obj:
+    str: value
+  arr:
+    - str1
+    - str2
+struct:
+  option: true
+
+---
+id: item2
+enum: 1
+data:
+  obj:
+    str: value
+  arr:
+    - str1
+    - str2
+struct:
+  option: true
diff --git a/assets/tests/setup/collections/collections.yaml b/assets/tests/setup/collections/collections.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..13c7fe7f4f3fd4806d52e29f8a2aa06ba40b94c4
--- /dev/null
+++ b/assets/tests/setup/collections/collections.yaml
@@ -0,0 +1,42 @@
+---
+type: object
+params:
+  inline: false
+  fields:
+    name:
+      title: Название
+      description: Название коллекции
+      type: string
+      params:
+        required: true
+metadata:
+  collection_id: collection_a
+  collection_name: Коллекция A
+---
+type: object
+params:
+  inline: false
+  fields:
+    name:
+      title: Название
+      description: Название коллекции
+      type: string
+      params:
+        required: true
+metadata:
+  collection_id: collection_b
+  collection_name: Коллекция B
+---
+id: collection_c
+name: Коллекция C
+schema:
+  type: object
+  params:
+    inline: false
+    fields:
+      name:
+        title: Название
+        description: Название коллекции
+        type: string
+        params:
+          required: true
\ No newline at end of file
diff --git a/assets_test.go b/assets_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..69a71b8a5323ca6bd8d7b103f66a70b62054ef29
--- /dev/null
+++ b/assets_test.go
@@ -0,0 +1,86 @@
+package perxis
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+type State int
+
+const (
+	State1 State = iota
+	State2
+)
+
+type testEntry struct {
+	ID     string
+	Enum   State
+	Data   map[string]interface{}
+	Struct *nested
+}
+
+type nested struct {
+	Option *bool
+}
+
+func TestFromFS(t *testing.T) {
+	tr := true
+	i1 := &testEntry{
+		ID:   "item1",
+		Enum: State2,
+		Data: map[string]interface{}{
+			"obj": map[string]interface{}{"str": "value"},
+			"arr": []interface{}{"str1", "str2"},
+		},
+		Struct: &nested{
+			Option: &tr,
+		},
+	}
+
+	i2 := *i1
+	i2.ID = "item2"
+
+	i3 := *i1
+	i3.ID = "item3"
+
+	assets := NewAssets[*testEntry]()
+	r, err := assets.FromFS(os.DirFS("assets/tests/assets"))
+	require.NoError(t, err)
+	require.Len(t, r, 3)
+	assert.ElementsMatch(t, []*testEntry{i1, &i2, &i3}, r)
+}
+
+func TestFrom(t *testing.T) {
+	tr := true
+	i1 := &testEntry{
+		ID:   "item1",
+		Enum: State2,
+		Data: map[string]interface{}{
+			"obj": map[string]interface{}{"str": "value"},
+			"arr": []interface{}{"str1", "str2"},
+		},
+		Struct: &nested{
+			Option: &tr,
+		},
+	}
+
+	i2 := *i1
+	i2.ID = "item2"
+
+	i3 := *i1
+	i3.ID = "item3"
+
+	assets := NewAssets[*testEntry]()
+	r, err := assets.From(os.DirFS("assets"), "tests/assets")
+	require.NoError(t, err)
+	require.Len(t, r, 3)
+	assert.ElementsMatch(t, []*testEntry{i1, &i2, &i3}, r)
+
+	r, err = assets.From(os.DirFS("assets"), "tests/assets/items.yaml")
+	require.NoError(t, err)
+	require.Len(t, r, 2)
+	assert.Equal(t, []*testEntry{i1, &i2}, r)
+}
diff --git a/cliff.toml b/cliff.toml
new file mode 100644
index 0000000000000000000000000000000000000000..78b82d95b808fd8b3a85e9013870059b8bb46b13
--- /dev/null
+++ b/cliff.toml
@@ -0,0 +1,107 @@
+# git-cliff ~ default configuration file
+# https://git-cliff.org/docs/configuration
+#
+# Lines starting with "#" are comments.
+# Configuration options are organized into tables and keys.
+# See documentation for more information on available options.
+[bump]
+features_always_bump_minor = true
+breaking_always_bump_major = true
+
+[changelog]
+# changelog header
+header = """
+# Changelog\n
+All notable changes to this project will be documented in this file.\n
+"""
+# template for the changelog body
+# https://keats.github.io/tera/docs/#introduction
+body = """
+{% if version %}\
+    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+    ## [unreleased]
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | striptags | trim | upper_first }}
+    {% for commit in commits %}
+        - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
+            {% if commit.breaking %}[**breaking**] {% endif %}\
+            {{ commit.message | upper_first }} \
+        {% if commit.links %}\
+       ({% for link in commit.links %}\
+          [{{ link.text }}]({{ link.href }})\
+          {% if not loop.last %}, {% endif %}\
+        {% endfor %})\
+        {% endif %}\
+        -([{{ commit.id | truncate(length=7, end="") }}]($REPO/-/commit/{{ commit.id }}))\
+    {% endfor %}
+{% endfor %}\n
+"""
+# template for the changelog footer
+footer = """
+<!-- generated by git-cliff -->
+"""
+# remove the leading and trailings
+trim = true
+# postprocessors
+postprocessors = [
+    { pattern = '\$REPO', replace = "https://git.perx.ru/perxis/perxis-go" },
+]
+
+[git]
+# parse the commits based on https://www.conventionalcommits.org
+conventional_commits = true
+# filter out the commits that are not conventional
+filter_unconventional = true
+# process each line of a commit as an individual commit
+split_commits = false
+# regex for preprocessing the commit messages
+commit_preprocessors = [
+    { pattern = 'Feat:', replace = "feat:"},
+    { pattern = 'WIP:', replace = ""},
+    { pattern = 'wip:', replace = ""}
+    # Replace issue numbers
+    #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
+    # Check spelling of the commit with https://github.com/crate-ci/typos
+    # If the spelling is incorrect, it will be automatically fixed.
+    #{ pattern = '.*', replace_command = 'typos --write-changes -' },
+]
+# regex for parsing and grouping commits
+commit_parsers = [
+    { message = "^feat", group = "<!-- 0 -->🚀 Features" },
+    { message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
+    { message = "^doc", group = "<!-- 3 -->📚 Documentation" },
+    { message = "^perf", group = "<!-- 4 -->⚡ Performance" },
+    { message = "^refactor", group = "<!-- 2 -->🚜 Refactor", skip = true },
+    { message = "^style", group = "<!-- 5 -->🎨 Styling" },
+    { message = "^test", group = "<!-- 6 -->🧪 Testing" },
+    { message = "^chore\\(release\\): prepare for", skip = true },
+    { message = "^chore\\(deps.*\\)", skip = true },
+    { message = "^chore\\(pr\\)", skip = true },
+    { message = "^chore\\(pull\\)", skip = true },
+    { message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
+    { body = ".*security", group = "<!-- 8 -->🛡️ Security" },
+    { message = "^revert", group = "<!-- 9 -->◀️ Revert" },
+    { message = "^irefac", skip = true },
+]
+# protect breaking changes from being skipped due to matching a skipping commit_parser
+protect_breaking_commits = false
+# filter out the commits that are not matched by commit parsers
+filter_commits = false
+# regex for matching git tags
+# tag_pattern = "v[0-9].*"
+# regex for skipping tags
+skip_tags = "v0.([0-9]\\.|1[0-8]).*"
+# regex for ignoring tags
+# ignore_tags = ""
+# sort the tags topologically
+topo_order = false
+# sort the commits inside sections by oldest/newest order
+sort_commits = "oldest"
+# limit the number of commits included in the changelog.
+# limit_commits = 42
+link_parsers = [
+    { pattern = "#(PRXS-(\\d+))", href = "https://tracker.yandex.ru/$1"},
+    { pattern = "RFC(\\d+)", text = "ietf-rfc$1", href = "https://datatracker.ietf.org/doc/html/rfc$1"},
+]
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 81d914bcbb61d73aadf2c8c1851bff6f660d05b5..372a5f1f61d2f523d327d630e73fa21f0aad2eac 100644
--- a/go.mod
+++ b/go.mod
@@ -17,15 +17,15 @@ require (
 	github.com/rs/xid v1.5.0
 	github.com/stretchr/testify v1.8.4
 	go.mongodb.org/mongo-driver v1.13.0
-	go.opentelemetry.io/otel v1.21.0
-	go.opentelemetry.io/otel/trace v1.21.0
+	go.opentelemetry.io/otel v1.24.0
+	go.opentelemetry.io/otel/trace v1.24.0
 	go.uber.org/zap v1.26.0
-	golang.org/x/crypto v0.15.0
+	golang.org/x/crypto v0.23.0
 	golang.org/x/image v0.14.0
-	golang.org/x/net v0.18.0
-	golang.org/x/oauth2 v0.14.0
-	google.golang.org/grpc v1.59.0
-	google.golang.org/protobuf v1.31.0
+	golang.org/x/net v0.25.0
+	golang.org/x/oauth2 v0.18.0
+	google.golang.org/grpc v1.64.0
+	google.golang.org/protobuf v1.34.1
 	gopkg.in/yaml.v3 v3.0.1
 )
 
@@ -35,15 +35,15 @@ require (
 )
 
 require (
-	cloud.google.com/go/compute v1.23.3 // indirect
+	cloud.google.com/go/compute v1.25.1 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
 	github.com/brianvoe/gofakeit/v6 v6.26.3
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/go-kit/log v0.2.1 // indirect
 	github.com/go-logfmt/logfmt v0.6.0 // indirect
-	github.com/go-logr/logr v1.3.0 // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
 	github.com/gosimple/unidecode v1.0.1 // indirect
 	github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -59,12 +59,12 @@ require (
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
-	go.opentelemetry.io/otel/metric v1.21.0
+	go.opentelemetry.io/otel/metric v1.24.0
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/sync v0.5.0 // indirect
-	golang.org/x/sys v0.14.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/sync v0.6.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/text v0.15.0
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 )
diff --git a/go.sum b/go.sum
index 4fa995ca097f0a961d0c4fc9d10cbe640e972c00..7f00103fa5b26228e50be943d05c23325cbdc38c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
-cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
+cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
+cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
 cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
 cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o=
@@ -21,14 +21,14 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
 github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
 github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
-github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -109,12 +109,12 @@ github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 go.mongodb.org/mongo-driver v1.13.0 h1:67DgFFjYOCMWdtTEmKFpV3ffWlFnh+CYZ8ZS/tXWUfY=
 go.mongodb.org/mongo-driver v1.13.0/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
-go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
-go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
-go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
-go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
-go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
-go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
+go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
+go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
+go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
+go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
+go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
+go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
 go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
 go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -125,8 +125,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
-golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
 golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
@@ -136,14 +136,14 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
-golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
-golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
-golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
+golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -151,8 +151,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
-golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -161,8 +161,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -170,14 +170,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
 google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e h1:Elxv5MwEkCI9f5SkoL6afed6NTdxaGoAo39eANBwHL8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/pkg/id/id.go b/id/id.go
similarity index 100%
rename from pkg/id/id.go
rename to id/id.go
diff --git a/pkg/id/id_test.go b/id/id_test.go
similarity index 100%
rename from pkg/id/id_test.go
rename to id/id_test.go
diff --git a/id/object_id_test.go b/id/object_id_test.go
deleted file mode 100644
index 56a2c104e000756640eefc978be025c01757d13b..0000000000000000000000000000000000000000
--- a/id/object_id_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package id
-
-import (
-	"testing"
-
-	"git.perx.ru/perxis/perxis-go/pkg/items"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func Test_ParseID(t *testing.T) {
-	tests := []struct {
-		name      string
-		id        any
-		result    *ObjectId
-		wantError bool
-	}{
-		{
-			name:   "SpaceId",
-			id:     "/spaces/<space_id>",
-			result: MustObjectId("/spaces/<space_id>"),
-		},
-		{
-			name:   "ServiceID",
-			id:     "/services/<service_id>",
-			result: MustObjectId("/services/<service_id>"),
-		},
-		{
-			name:   "UserID",
-			id:     "/users/<user_id>",
-			result: MustObjectId("/users/<user_id>"),
-		},
-		{
-			name:   "OrganizationID",
-			id:     "/orgs/<org_id>",
-			result: MustObjectId("/orgs/<org_id>"),
-		},
-		{
-			name:   "ClientID",
-			id:     "/spaces/<space_id>/clients/<client_id>",
-			result: MustObjectId("/spaces/<space_id>/clients/<client_id>"),
-		},
-		{
-			name:   "RoleID",
-			id:     "/spaces/<space_id>/roles/<role_id>",
-			result: MustObjectId("/spaces/<space_id>/roles/<role_id>"),
-		},
-		{
-			name:   "LocaleID",
-			id:     "/spaces/<space_id>/locales/<locale_id>",
-			result: MustObjectId("/spaces/<space_id>/locales/<locale_id>"),
-		},
-		{
-			name:   "EnvironmentID",
-			id:     "/spaces/<space_id>/envs/<env_id>",
-			result: MustObjectId("/spaces/<space_id>/envs/<env_id>"),
-		},
-		{
-			name:   "CollectionId",
-			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>",
-			result: MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>"),
-		},
-		{
-			name:   "SchemaID",
-			id:     "/spaces/<space_id>/envs/<env_id>/schema/<collection_id>",
-			result: MustObjectId("/spaces/<space_id>/envs/<env_id>/schema/<collection_id>"),
-		},
-		{
-			name:   "ItemId",
-			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>",
-			result: MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>"),
-		},
-		{
-			name:   "RevisionID",
-			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/revs/<rev_id>",
-			result: MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/revs/<rev_id>"),
-		},
-		{
-			name:   "FieldId",
-			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/fields/<field_name>",
-			result: MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/fields/<field_name>"),
-		},
-		{
-			name:      "With error #1: no backslash in the beginning of id",
-			id:        "spaces/<space_id>",
-			result:    nil,
-			wantError: true,
-		},
-		{
-			name:      "With error #2: backslash in the end of id",
-			id:        "/spaces/<space_id>/",
-			result:    nil,
-			wantError: true,
-		},
-		{
-			name:      "With error #3: typo in 'spaces'",
-			id:        "/space/<space_id>",
-			result:    nil,
-			wantError: true,
-		},
-		{
-			name:      "With error #4: no space_id in id",
-			id:        "/spaces",
-			result:    nil,
-			wantError: true,
-		},
-		{
-			name:      "With error #5: multiple backslashes in the end of id",
-			id:        "/spaces/<space_id>///",
-			result:    nil,
-			wantError: true,
-		},
-		{
-			name:      "With error #6: nil value",
-			id:        nil,
-			wantError: true,
-		},
-		{
-			name:      "With error #7: nil object value",
-			id:        (*items.Item)(nil),
-			wantError: true,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			id, err := NewObjectId(tt.id)
-			if tt.wantError {
-				require.Error(t, err)
-				return
-			}
-			require.NoError(t, err)
-			require.Equal(t, tt.result, id)
-			require.Equal(t, tt.id, id.String())
-		})
-	}
-}
-
-func Test_Map(t *testing.T) {
-	tests := []struct {
-		name string
-		id   *ObjectId
-	}{
-		{
-			name: "ServiceID",
-			id:   &ObjectId{Descriptor: &ServiceId{ServiceID: "<service_id>"}},
-		},
-		{
-			name: "UserID",
-			id:   &ObjectId{Descriptor: &UserId{UserID: "<user_id>"}},
-		},
-		{
-			name: "OrganizationID",
-			id:   &ObjectId{Descriptor: &OrganizationId{OrganizationID: "<org_id>"}},
-		},
-		{
-			name: "SpaceId",
-			id:   &ObjectId{Descriptor: &SpaceId{SpaceID: "<space_id>"}},
-		},
-		{
-			name: "ClientID",
-			id: &ObjectId{Descriptor: &ClientId{
-				SpaceId:  SpaceId{SpaceID: "<space_id>"},
-				ClientID: "<client_id>",
-			}},
-		},
-		{
-			name: "RoleID",
-			id: &ObjectId{Descriptor: &RoleId{
-				SpaceId: SpaceId{SpaceID: "<space_id>"},
-				RoleID:  "<role_id>",
-			}},
-		},
-		{
-			name: "LocaleID",
-			id: &ObjectId{Descriptor: &LocaleID{
-				SpaceId:  SpaceId{SpaceID: "<space_id>"},
-				LocaleID: "<locale_id>",
-			}},
-		},
-		{
-			name: "EnvironmentID",
-			id: &ObjectId{Descriptor: &EnvironmentId{
-				SpaceId:       SpaceId{SpaceID: "<space_id>"},
-				EnvironmentID: "<env_id>",
-			}},
-		},
-		{
-			name: "CollectionId",
-			id: &ObjectId{Descriptor: &CollectionId{
-				EnvironmentId: EnvironmentId{
-					SpaceId:       SpaceId{SpaceID: "<space_id>"},
-					EnvironmentID: "<env_id>",
-				},
-				CollectionID: "<collection_id>",
-			}},
-		},
-		{
-			name: "Schema ID",
-			id: &ObjectId{Descriptor: &SchemaId{
-				EnvironmentId: EnvironmentId{
-					SpaceId:       SpaceId{SpaceID: "<space_id>"},
-					EnvironmentID: "<env_id>",
-				},
-				CollectionID: "<collection_id>",
-			}},
-		},
-		{
-			name: "ItemId",
-			id: &ObjectId{Descriptor: &ItemId{
-				CollectionId: CollectionId{
-					EnvironmentId: EnvironmentId{
-						SpaceId:       SpaceId{SpaceID: "<space_id>"},
-						EnvironmentID: "<env_id>",
-					},
-					CollectionID: "<collection_id>",
-				},
-				ItemID: "<item_id>",
-			}},
-		},
-		{
-			name: "RevisionID",
-			id: &ObjectId{Descriptor: &RevisionId{
-				ItemId: ItemId{
-					CollectionId: CollectionId{
-						EnvironmentId: EnvironmentId{
-							SpaceId:       SpaceId{SpaceID: "<space_id>"},
-							EnvironmentID: "<env_id>",
-						},
-						CollectionID: "<collection_id>",
-					},
-					ItemID: "<item_id>",
-				},
-				RevisionID: "<rev_id>",
-			}},
-		},
-		{
-			name: "FieldId",
-			id: &ObjectId{Descriptor: &FieldId{
-				ItemId: ItemId{
-					CollectionId: CollectionId{
-						EnvironmentId: EnvironmentId{
-							SpaceId:       SpaceId{SpaceID: "<space_id>"},
-							EnvironmentID: "<env_id>",
-						},
-						CollectionID: "<collection_id>",
-					},
-					ItemID: "<item_id>",
-				},
-				Field: "<field_name>",
-			}},
-		},
-		{
-			name: "SystemID",
-			id:   &ObjectId{Descriptor: &SystemId{}},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			v, err := FromMap(tt.id.Map())
-			require.NoError(t, err)
-			assert.Equal(t, tt.id, v, "проверка FromMap для типа ID, должен быть равен исходному значению")
-			assert.Equal(t, v.Map(), tt.id.Map())
-		})
-	}
-}
diff --git a/id/test/object_id_test.go b/id/test/object_id_test.go
index e29328ddf25f799cbb56707fa292762f69c39207..2f01fa125e376623ceb710bbe594235b8434d853 100644
--- a/id/test/object_id_test.go
+++ b/id/test/object_id_test.go
@@ -14,6 +14,7 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/roles"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"git.perx.ru/perxis/perxis-go/pkg/users"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
@@ -886,3 +887,259 @@ func Test_LocaleId(t *testing.T) {
 		})
 	}
 }
+
+func Test_ParseID(t *testing.T) {
+	tests := []struct {
+		name      string
+		id        any
+		result    *id.ObjectId
+		wantError bool
+	}{
+		{
+			name:   "SpaceId",
+			id:     "/spaces/<space_id>",
+			result: id.MustObjectId("/spaces/<space_id>"),
+		},
+		{
+			name:   "ServiceID",
+			id:     "/services/<service_id>",
+			result: id.MustObjectId("/services/<service_id>"),
+		},
+		{
+			name:   "UserID",
+			id:     "/users/<user_id>",
+			result: id.MustObjectId("/users/<user_id>"),
+		},
+		{
+			name:   "OrganizationID",
+			id:     "/orgs/<org_id>",
+			result: id.MustObjectId("/orgs/<org_id>"),
+		},
+		{
+			name:   "ClientID",
+			id:     "/spaces/<space_id>/clients/<client_id>",
+			result: id.MustObjectId("/spaces/<space_id>/clients/<client_id>"),
+		},
+		{
+			name:   "RoleID",
+			id:     "/spaces/<space_id>/roles/<role_id>",
+			result: id.MustObjectId("/spaces/<space_id>/roles/<role_id>"),
+		},
+		{
+			name:   "LocaleID",
+			id:     "/spaces/<space_id>/locales/<locale_id>",
+			result: id.MustObjectId("/spaces/<space_id>/locales/<locale_id>"),
+		},
+		{
+			name:   "EnvironmentID",
+			id:     "/spaces/<space_id>/envs/<env_id>",
+			result: id.MustObjectId("/spaces/<space_id>/envs/<env_id>"),
+		},
+		{
+			name:   "CollectionId",
+			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>",
+			result: id.MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>"),
+		},
+		{
+			name:   "SchemaID",
+			id:     "/spaces/<space_id>/envs/<env_id>/schema/<collection_id>",
+			result: id.MustObjectId("/spaces/<space_id>/envs/<env_id>/schema/<collection_id>"),
+		},
+		{
+			name:   "ItemId",
+			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>",
+			result: id.MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>"),
+		},
+		{
+			name:   "RevisionID",
+			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/revs/<rev_id>",
+			result: id.MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/revs/<rev_id>"),
+		},
+		{
+			name:   "FieldId",
+			id:     "/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/fields/<field_name>",
+			result: id.MustObjectId("/spaces/<space_id>/envs/<env_id>/cols/<collection_id>/items/<item_id>/fields/<field_name>"),
+		},
+		{
+			name:      "With error #1: no backslash in the beginning of id",
+			id:        "spaces/<space_id>",
+			result:    nil,
+			wantError: true,
+		},
+		{
+			name:      "With error #2: backslash in the end of id",
+			id:        "/spaces/<space_id>/",
+			result:    nil,
+			wantError: true,
+		},
+		{
+			name:      "With error #3: typo in 'spaces'",
+			id:        "/space/<space_id>",
+			result:    nil,
+			wantError: true,
+		},
+		{
+			name:      "With error #4: no space_id in id",
+			id:        "/spaces",
+			result:    nil,
+			wantError: true,
+		},
+		{
+			name:      "With error #5: multiple backslashes in the end of id",
+			id:        "/spaces/<space_id>///",
+			result:    nil,
+			wantError: true,
+		},
+		{
+			name:      "With error #6: nil value",
+			id:        nil,
+			wantError: true,
+		},
+		{
+			name:      "With error #7: nil object value",
+			id:        (*items.Item)(nil),
+			wantError: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			id, err := id.NewObjectId(tt.id)
+			if tt.wantError {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+			require.Equal(t, tt.result, id)
+			require.Equal(t, tt.id, id.String())
+		})
+	}
+}
+
+func Test_Map(t *testing.T) {
+	tests := []struct {
+		name string
+		id   *id.ObjectId
+	}{
+		{
+			name: "ServiceID",
+			id:   &id.ObjectId{Descriptor: &id.ServiceId{ServiceID: "<service_id>"}},
+		},
+		{
+			name: "UserID",
+			id:   &id.ObjectId{Descriptor: &id.UserId{UserID: "<user_id>"}},
+		},
+		{
+			name: "OrganizationID",
+			id:   &id.ObjectId{Descriptor: &id.OrganizationId{OrganizationID: "<org_id>"}},
+		},
+		{
+			name: "SpaceId",
+			id:   &id.ObjectId{Descriptor: &id.SpaceId{SpaceID: "<space_id>"}},
+		},
+		{
+			name: "ClientID",
+			id: &id.ObjectId{Descriptor: &id.ClientId{
+				SpaceId:  id.SpaceId{SpaceID: "<space_id>"},
+				ClientID: "<client_id>",
+			}},
+		},
+		{
+			name: "RoleID",
+			id: &id.ObjectId{Descriptor: &id.RoleId{
+				SpaceId: id.SpaceId{SpaceID: "<space_id>"},
+				RoleID:  "<role_id>",
+			}},
+		},
+		{
+			name: "LocaleID",
+			id: &id.ObjectId{Descriptor: &id.LocaleID{
+				SpaceId:  id.SpaceId{SpaceID: "<space_id>"},
+				LocaleID: "<locale_id>",
+			}},
+		},
+		{
+			name: "EnvironmentID",
+			id: &id.ObjectId{Descriptor: &id.EnvironmentId{
+				SpaceId:       id.SpaceId{SpaceID: "<space_id>"},
+				EnvironmentID: "<env_id>",
+			}},
+		},
+		{
+			name: "CollectionId",
+			id: &id.ObjectId{Descriptor: &id.CollectionId{
+				EnvironmentId: id.EnvironmentId{
+					SpaceId:       id.SpaceId{SpaceID: "<space_id>"},
+					EnvironmentID: "<env_id>",
+				},
+				CollectionID: "<collection_id>",
+			}},
+		},
+		{
+			name: "Schema ID",
+			id: &id.ObjectId{Descriptor: &id.SchemaId{
+				EnvironmentId: id.EnvironmentId{
+					SpaceId:       id.SpaceId{SpaceID: "<space_id>"},
+					EnvironmentID: "<env_id>",
+				},
+				CollectionID: "<collection_id>",
+			}},
+		},
+		{
+			name: "ItemId",
+			id: &id.ObjectId{Descriptor: &id.ItemId{
+				CollectionId: id.CollectionId{
+					EnvironmentId: id.EnvironmentId{
+						SpaceId:       id.SpaceId{SpaceID: "<space_id>"},
+						EnvironmentID: "<env_id>",
+					},
+					CollectionID: "<collection_id>",
+				},
+				ItemID: "<item_id>",
+			}},
+		},
+		{
+			name: "RevisionID",
+			id: &id.ObjectId{Descriptor: &id.RevisionId{
+				ItemId: id.ItemId{
+					CollectionId: id.CollectionId{
+						EnvironmentId: id.EnvironmentId{
+							SpaceId:       id.SpaceId{SpaceID: "<space_id>"},
+							EnvironmentID: "<env_id>",
+						},
+						CollectionID: "<collection_id>",
+					},
+					ItemID: "<item_id>",
+				},
+				RevisionID: "<rev_id>",
+			}},
+		},
+		{
+			name: "FieldId",
+			id: &id.ObjectId{Descriptor: &id.FieldId{
+				ItemId: id.ItemId{
+					CollectionId: id.CollectionId{
+						EnvironmentId: id.EnvironmentId{
+							SpaceId:       id.SpaceId{SpaceID: "<space_id>"},
+							EnvironmentID: "<env_id>",
+						},
+						CollectionID: "<collection_id>",
+					},
+					ItemID: "<item_id>",
+				},
+				Field: "<field_name>",
+			}},
+		},
+		{
+			name: "SystemID",
+			id:   &id.ObjectId{Descriptor: &id.SystemId{}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v, err := id.FromMap(tt.id.Map())
+			require.NoError(t, err)
+			assert.Equal(t, tt.id, v, "проверка FromMap для типа ID, должен быть равен исходному значению")
+			assert.Equal(t, v.Map(), tt.id.Map())
+		})
+	}
+}
diff --git a/images/convert/convert.go b/images/convert/convert.go
index b704055dd7399eb4629d1804cd51574befff49c6..0903f19f4e5c3808f750e9c4e3bc4b7b0f81c409 100644
--- a/images/convert/convert.go
+++ b/images/convert/convert.go
@@ -3,6 +3,7 @@ package convert
 import (
 	"image"
 	"io"
+	"mime"
 	"os"
 	"strings"
 
@@ -69,5 +70,23 @@ func FormatFromExtension(ext string) (Format, error) {
 	if f, ok := formatExtensions[ext]; ok {
 		return f, nil
 	}
-	return "", errors.Errorf("unsupported format")
+	return "", errors.Errorf("unsupported format: %s", ext)
+}
+
+func FormatFromMIMEType(typ string) (Format, error) {
+	extensions, err := mime.ExtensionsByType(typ)
+	if err != nil {
+		return "", errors.Wrap(err, "get extensions by mime type")
+	}
+	if len(extensions) == 0 {
+		return "", errors.Errorf("unknown mime type: %s", typ)
+	}
+	for _, extension := range extensions {
+		var format Format
+		format, err = FormatFromExtension(extension)
+		if err == nil {
+			return format, nil
+		}
+	}
+	return "", err
 }
diff --git a/images/convert/convert_test.go b/images/convert/convert_test.go
index b7a759eac8dba586d985806bb587975e07a93403..e48930e7de8000e8a43ecc7277c808340a1fb553 100644
--- a/images/convert/convert_test.go
+++ b/images/convert/convert_test.go
@@ -57,6 +57,55 @@ func TestFormatFromExtension(t *testing.T) {
 	}
 }
 
+func TestFormatFromMIMEType(t *testing.T) {
+	var tests = []struct {
+		name    string
+		input   string
+		output  Format
+		wantErr bool
+	}{
+		{
+			name:    "correct jpeg",
+			input:   "image/jpeg",
+			output:  JPEG,
+			wantErr: false,
+		},
+		{
+			name:    "correct png",
+			input:   "image/png",
+			output:  PNG,
+			wantErr: false,
+		},
+		{
+			name:    "unsupported mime type",
+			input:   "application/json",
+			wantErr: true,
+		},
+		{
+			name:    "unknown mime type",
+			input:   "application/perxis",
+			wantErr: true,
+		},
+		{
+			name:    "no mime type",
+			input:   "",
+			wantErr: true,
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			format, err := FormatFromMIMEType(tc.input)
+			if tc.wantErr {
+				require.Error(t, err)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tc.output, format)
+			}
+		})
+	}
+}
+
 func TestEncode(t *testing.T) {
 	var tests = []struct {
 		name    string
diff --git a/images/transport/client.microgen.go b/images/transport/client.go
similarity index 68%
rename from images/transport/client.microgen.go
rename to images/transport/client.go
index d8b9342e08a9770c17cbc81ead84eddf57dd16fa..bfa7487ca160b8098337e4ebb885c74424aecd24 100644
--- a/images/transport/client.microgen.go
+++ b/images/transport/client.go
@@ -4,12 +4,9 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	images "git.perx.ru/perxis/perxis-go/images"
 	files "git.perx.ru/perxis/perxis-go/pkg/files"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Get(arg0 context.Context, arg1 *files.File, arg2 *images.GetOptions) (res0 *files.File, res1 error) {
@@ -19,9 +16,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 *files.File, arg2 *images
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Result, res1
diff --git a/images/transport/grpc/client.go b/images/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..167b69288bc0642221a70bce8a1e1f236ef48db9
--- /dev/null
+++ b/images/transport/grpc/client.go
@@ -0,0 +1,17 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/images/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		GetEndpoint: grpcerr.ClientMiddleware(c.GetEndpoint),
+	}
+}
diff --git a/images/transport/grpc/protobuf_type_converters.microgen.go b/images/transport/grpc/protobuf_type_converters.microgen.go
index 48a13510667582f7858cbc04ff517598eb17772d..51f6d68668be2c416501fadbe14dbaeead5308df 100644
--- a/images/transport/grpc/protobuf_type_converters.microgen.go
+++ b/images/transport/grpc/protobuf_type_converters.microgen.go
@@ -18,7 +18,7 @@ func PtrFileFileToProto(source *file.File) (*pbfile.File, error) {
 	pbFile := &pbfile.File{
 		Id:       source.ID,
 		Name:     source.Name,
-		Size:     int32(source.Size),
+		Size:     source.Size,
 		MimeType: source.MimeType,
 		Url:      source.URL,
 	}
@@ -32,7 +32,7 @@ func ProtoToPtrFileFile(protoSource *pbfile.File) (*file.File, error) {
 	file := &file.File{
 		ID:       protoSource.Id,
 		Name:     protoSource.Name,
-		Size:     int(protoSource.Size),
+		Size:     protoSource.Size,
 		MimeType: protoSource.MimeType,
 		URL:      protoSource.Url,
 	}
diff --git a/images/transport/grpc/server.go b/images/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..c36a57b0eadbd8878539d22d33cf57f7329b6b65
--- /dev/null
+++ b/images/transport/grpc/server.go
@@ -0,0 +1,17 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/images"
+	"git.perx.ru/perxis/perxis-go/images/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	pb "git.perx.ru/perxis/perxis-go/proto/images"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc images.Images, opts ...grpckit.ServerOption) pb.ImagesServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		GetEndpoint: grpcerr.ServerMiddleware(eps.GetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/logs/log.go b/logs/log.go
index 2de45a30cd98bbd5ce840726b862dbca52fc8480..60f88d3a21869fef323c489af6e45858b3960124 100644
--- a/logs/log.go
+++ b/logs/log.go
@@ -27,17 +27,18 @@ func (l Level) String() string {
 }
 
 type Entry struct {
-	ID        string       `json:"id" bson:"_id" mapstructure:"id"`
-	Timestamp time.Time    `json:"timestamp,omitempty" bson:"timestamp,omitempty" mapstructure:"timestamp,omitempty"`
-	Level     Level        `json:"level,omitempty" bson:"level,omitempty" mapstructure:"level,omitempty"`
-	Message   string       `json:"message,omitempty" bson:"message,omitempty" mapstructure:"message,omitempty"`
-	Category  string       `json:"category,omitempty" bson:"category,omitempty" mapstructure:"category,omitempty"`
-	Component string       `json:"component,omitempty" bson:"component,omitempty" mapstructure:"component,omitempty"`
-	Event     string       `json:"event,omitempty" bson:"event,omitempty" mapstructure:"event,omitempty"`
-	ObjectID  *id.ObjectId `json:"object_id,omitempty" bson:"object_id,omitempty" mapstructure:"object_id,omitempty"`
-	CallerID  *id.ObjectId `json:"caller_id,omitempty" bson:"caller_id,omitempty" mapstructure:"caller_id,omitempty"`
-	Attr      interface{}  `json:"attr,omitempty" bson:"attr,omitempty" mapstructure:"attr,omitempty"`
-	Tags      []string     `json:"tags,omitempty" bson:"tags,omitempty" mapstructure:"tags,omitempty"`
+	ID          string       `json:"id" bson:"_id" mapstructure:"id"`
+	Timestamp   time.Time    `json:"timestamp,omitempty" bson:"timestamp,omitempty" mapstructure:"timestamp,omitempty"`
+	Level       Level        `json:"level,omitempty" bson:"level,omitempty" mapstructure:"level,omitempty"`
+	Message     string       `json:"message,omitempty" bson:"message,omitempty" mapstructure:"message,omitempty"`
+	Category    string       `json:"category,omitempty" bson:"category,omitempty" mapstructure:"category,omitempty"`
+	Component   string       `json:"component,omitempty" bson:"component,omitempty" mapstructure:"component,omitempty"`
+	Event       string       `json:"event,omitempty" bson:"event,omitempty" mapstructure:"event,omitempty"`
+	ObjectID    *id.ObjectId `json:"object_id,omitempty" bson:"object_id,omitempty" mapstructure:"object_id,omitempty"`
+	CallerID    *id.ObjectId `json:"caller_id,omitempty" bson:"caller_id,omitempty" mapstructure:"caller_id,omitempty"`
+	Attr        interface{}  `json:"attr,omitempty" bson:"attr,omitempty" mapstructure:"attr,omitempty"`
+	Tags        []string     `json:"tags,omitempty" bson:"tags,omitempty" mapstructure:"tags,omitempty"`
+	SearchScore float64      `json:"searchScore,omitempty" bson:"search_score,omitempty"`
 }
 
 //func convertInterfaceToAny(v interface{}) (*any.Any, error) {
@@ -52,15 +53,16 @@ type Entry struct {
 
 func EntryToPB(entry *Entry) *pb.LogEntry {
 	logEntry := &pb.LogEntry{
-		Id:        entry.ID,
-		Timestamp: timestamppb.New(entry.Timestamp),
-		Level:     pb.LogLevel(entry.Level),
-		Message:   entry.Message,
-		Category:  entry.Category,
-		Component: entry.Component,
-		Event:     entry.Event,
-		Attr:      nil, //implement
-		Tags:      entry.Tags,
+		Id:          entry.ID,
+		Timestamp:   timestamppb.New(entry.Timestamp),
+		Level:       pb.LogLevel(entry.Level),
+		Message:     entry.Message,
+		Category:    entry.Category,
+		Component:   entry.Component,
+		Event:       entry.Event,
+		Attr:        nil, //implement
+		Tags:        entry.Tags,
+		SearchScore: entry.SearchScore,
 	}
 	if entry.ObjectID != nil {
 		logEntry.ObjectId = entry.ObjectID.String()
@@ -74,13 +76,14 @@ func EntryToPB(entry *Entry) *pb.LogEntry {
 
 func EntryFromPB(request *pb.LogEntry) *Entry {
 	logEntry := &Entry{
-		ID:        request.Id,
-		Timestamp: request.Timestamp.AsTime(),
-		Level:     Level(request.Level),
-		Message:   request.Message,
-		Category:  request.Category,
-		Component: request.Component,
-		Event:     request.Event,
+		ID:          request.Id,
+		Timestamp:   request.Timestamp.AsTime(),
+		Level:       Level(request.Level),
+		Message:     request.Message,
+		Category:    request.Category,
+		Component:   request.Component,
+		Event:       request.Event,
+		SearchScore: request.SearchScore,
 	}
 
 	if request.ObjectId != "" {
diff --git a/logs/mocks/Service.go b/logs/mocks/Service.go
index af4da73bc4d4c7b088fbfea7e7babc4c1ba30e36..0031c92c3b99e49348bf5c14c54a909f2e9736a7 100644
--- a/logs/mocks/Service.go
+++ b/logs/mocks/Service.go
@@ -5,7 +5,7 @@ package mocks
 import (
 	context "context"
 
-	log2 "git.perx.ru/perxis/perxis-go/logs"
+	logs "git.perx.ru/perxis/perxis-go/logs"
 	mock "github.com/stretchr/testify/mock"
 
 	options "git.perx.ru/perxis/perxis-go/pkg/options"
@@ -17,7 +17,7 @@ type Service struct {
 }
 
 // Delete provides a mock function with given fields: ctx, filter
-func (_m *Service) Delete(ctx context.Context, filter *log2.Filter) error {
+func (_m *Service) Delete(ctx context.Context, filter *logs.Filter) error {
 	ret := _m.Called(ctx, filter)
 
 	if len(ret) == 0 {
@@ -25,7 +25,7 @@ func (_m *Service) Delete(ctx context.Context, filter *log2.Filter) error {
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *log2.Filter) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *logs.Filter) error); ok {
 		r0 = rf(ctx, filter)
 	} else {
 		r0 = ret.Error(0)
@@ -35,27 +35,27 @@ func (_m *Service) Delete(ctx context.Context, filter *log2.Filter) error {
 }
 
 // Find provides a mock function with given fields: ctx, filter, _a2
-func (_m *Service) Find(ctx context.Context, filter *log2.Filter, _a2 *options.FindOptions) (*log2.FindResult, error) {
+func (_m *Service) Find(ctx context.Context, filter *logs.Filter, _a2 *options.FindOptions) (*logs.FindResult, error) {
 	ret := _m.Called(ctx, filter, _a2)
 
 	if len(ret) == 0 {
 		panic("no return value specified for Find")
 	}
 
-	var r0 *log2.FindResult
+	var r0 *logs.FindResult
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, *log2.Filter, *options.FindOptions) (*log2.FindResult, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *logs.Filter, *options.FindOptions) (*logs.FindResult, error)); ok {
 		return rf(ctx, filter, _a2)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *log2.Filter, *options.FindOptions) *log2.FindResult); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *logs.Filter, *options.FindOptions) *logs.FindResult); ok {
 		r0 = rf(ctx, filter, _a2)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*log2.FindResult)
+			r0 = ret.Get(0).(*logs.FindResult)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *log2.Filter, *options.FindOptions) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *logs.Filter, *options.FindOptions) error); ok {
 		r1 = rf(ctx, filter, _a2)
 	} else {
 		r1 = ret.Error(1)
@@ -65,7 +65,7 @@ func (_m *Service) Find(ctx context.Context, filter *log2.Filter, _a2 *options.F
 }
 
 // Log provides a mock function with given fields: ctx, entries
-func (_m *Service) Log(ctx context.Context, entries []*log2.Entry) error {
+func (_m *Service) Log(ctx context.Context, entries []*logs.Entry) error {
 	ret := _m.Called(ctx, entries)
 
 	if len(ret) == 0 {
@@ -73,7 +73,7 @@ func (_m *Service) Log(ctx context.Context, entries []*log2.Entry) error {
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, []*log2.Entry) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, []*logs.Entry) error); ok {
 		r0 = rf(ctx, entries)
 	} else {
 		r0 = ret.Error(0)
diff --git a/logs/mocks/Storage.go b/logs/mocks/Storage.go
index 87a6376d47ee44ece2f9d2458ee05e48a162b8ca..e341b0263c2570f508c4088ed1d9d2408b313e18 100644
--- a/logs/mocks/Storage.go
+++ b/logs/mocks/Storage.go
@@ -5,7 +5,7 @@ package mocks
 import (
 	context "context"
 
-	log2 "git.perx.ru/perxis/perxis-go/logs"
+	logs "git.perx.ru/perxis/perxis-go/logs"
 	mock "github.com/stretchr/testify/mock"
 
 	options "git.perx.ru/perxis/perxis-go/pkg/options"
@@ -17,7 +17,7 @@ type Storage struct {
 }
 
 // Delete provides a mock function with given fields: ctx, filter
-func (_m *Storage) Delete(ctx context.Context, filter *log2.Filter) error {
+func (_m *Storage) Delete(ctx context.Context, filter *logs.Filter) error {
 	ret := _m.Called(ctx, filter)
 
 	if len(ret) == 0 {
@@ -25,7 +25,7 @@ func (_m *Storage) Delete(ctx context.Context, filter *log2.Filter) error {
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *log2.Filter) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *logs.Filter) error); ok {
 		r0 = rf(ctx, filter)
 	} else {
 		r0 = ret.Error(0)
@@ -35,34 +35,34 @@ func (_m *Storage) Delete(ctx context.Context, filter *log2.Filter) error {
 }
 
 // Find provides a mock function with given fields: ctx, filter, _a2
-func (_m *Storage) Find(ctx context.Context, filter *log2.Filter, _a2 *options.FindOptions) ([]*log2.Entry, int, error) {
+func (_m *Storage) Find(ctx context.Context, filter *logs.Filter, _a2 *options.FindOptions) ([]*logs.Entry, int, error) {
 	ret := _m.Called(ctx, filter, _a2)
 
 	if len(ret) == 0 {
 		panic("no return value specified for Find")
 	}
 
-	var r0 []*log2.Entry
+	var r0 []*logs.Entry
 	var r1 int
 	var r2 error
-	if rf, ok := ret.Get(0).(func(context.Context, *log2.Filter, *options.FindOptions) ([]*log2.Entry, int, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *logs.Filter, *options.FindOptions) ([]*logs.Entry, int, error)); ok {
 		return rf(ctx, filter, _a2)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *log2.Filter, *options.FindOptions) []*log2.Entry); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *logs.Filter, *options.FindOptions) []*logs.Entry); ok {
 		r0 = rf(ctx, filter, _a2)
 	} else {
 		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]*log2.Entry)
+			r0 = ret.Get(0).([]*logs.Entry)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *log2.Filter, *options.FindOptions) int); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *logs.Filter, *options.FindOptions) int); ok {
 		r1 = rf(ctx, filter, _a2)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	if rf, ok := ret.Get(2).(func(context.Context, *log2.Filter, *options.FindOptions) error); ok {
+	if rf, ok := ret.Get(2).(func(context.Context, *logs.Filter, *options.FindOptions) error); ok {
 		r2 = rf(ctx, filter, _a2)
 	} else {
 		r2 = ret.Error(2)
@@ -90,7 +90,7 @@ func (_m *Storage) Init(ctx context.Context) error {
 }
 
 // Log provides a mock function with given fields: ctx, entry
-func (_m *Storage) Log(ctx context.Context, entry []*log2.Entry) error {
+func (_m *Storage) Log(ctx context.Context, entry []*logs.Entry) error {
 	ret := _m.Called(ctx, entry)
 
 	if len(ret) == 0 {
@@ -98,7 +98,7 @@ func (_m *Storage) Log(ctx context.Context, entry []*log2.Entry) error {
 	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, []*log2.Entry) error); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, []*logs.Entry) error); ok {
 		r0 = rf(ctx, entry)
 	} else {
 		r0 = ret.Error(0)
diff --git a/logs/zap/buffered_write_syncer.go b/logs/zap/buffered_write_syncer.go
index e862a79d115ecd82bc09622276d4194d39715345..42fae2b134e958045ba2a5986499eac4834862f3 100644
--- a/logs/zap/buffered_write_syncer.go
+++ b/logs/zap/buffered_write_syncer.go
@@ -71,28 +71,40 @@ func (ws *BufferedWriteSyncer) start() {
 	ws.buffer = make([]*logs.Entry, 0, ws.MaxBufferSize)
 	ws.syncQueue = make(chan []*logs.Entry, ws.MaxSyncQueueSize)
 	ws.flushStop = make(chan struct{})
+	ws.started = true
 
 	ws.wg.Add(2)
 	go ws.syncLoop()
 	go ws.flushLoop()
-
-	ws.started = true
 }
 
+// Stop останавливает все фоновые работы и синхронизирует оставшиеся записи
 func (ws *BufferedWriteSyncer) Stop() error {
-	ws.mu.Lock()
-	defer ws.mu.Unlock()
+	// Создаем новую область видимости для блокировки мьютекса только в указанной секции
+	// Это необходимо, чтобы предотвратить возможный deadlock, который может возникнуть
+	// если после блокировки мьютекса при остановке будет вызван Sync внутри flushLoop(),
+	// который будет ожидать освобождения мьютекса
+	stopped, err := func() (bool, error) {
+		ws.mu.Lock()
+		defer ws.mu.Unlock()
+
+		if !ws.started || ws.stopped {
+			return false, nil
+		}
+		ws.stopped = true
 
-	if !ws.started || ws.stopped {
-		return nil
-	}
-	ws.stopped = true
+		close(ws.flushStop) // завершаем flushLoop
+
+		err := ws.flush() // очищаем оставшиеся записи
 
-	close(ws.flushStop) // завершаем flushLoop
+		close(ws.syncQueue) // завершаем syncLoop
 
-	err := ws.flush() // очищаем оставшиеся записи
+		return true, err
+	}()
 
-	close(ws.syncQueue) // завершаем syncLoop
+	if !stopped {
+		return nil
+	}
 
 	ws.wg.Wait() // дожидаемся завершения flushLoop и syncLoop
 
@@ -127,7 +139,7 @@ func (ws *BufferedWriteSyncer) Sync() error {
 	ws.mu.Lock()
 	defer ws.mu.Unlock()
 
-	if ws.started {
+	if ws.started && !ws.stopped {
 		return ws.flush()
 	}
 
diff --git a/logs/zap/entry_encoder.go b/logs/zap/entry_encoder.go
index d7db9b301a7a41c9b869ddcc0c06146afafe9780..e53171c69dc21a0f9b0b0c82e91f087e780c8017 100644
--- a/logs/zap/entry_encoder.go
+++ b/logs/zap/entry_encoder.go
@@ -4,9 +4,8 @@ import (
 	"fmt"
 	"slices"
 
-	oid "git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/id"
 	"git.perx.ru/perxis/perxis-go/logs"
-	"git.perx.ru/perxis/perxis-go/pkg/id"
 	"git.perx.ru/perxis/perxis-go/zap"
 	"go.uber.org/zap/zapcore"
 )
@@ -60,9 +59,9 @@ func (enc *entryEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field
 		case "event":
 			ent.Event = clone.fields[i].String
 		case "object":
-			ent.ObjectID, _ = clone.fields[i].Interface.(*oid.ObjectId)
+			ent.ObjectID, _ = clone.fields[i].Interface.(*id.ObjectId)
 		case "caller":
-			ent.CallerID, _ = clone.fields[i].Interface.(*oid.ObjectId)
+			ent.CallerID, _ = clone.fields[i].Interface.(*id.ObjectId)
 		case "attr":
 			ent.Attr = clone.fields[i].Interface
 		case "error":
diff --git a/logs/zap/entry_encoder_slow.go b/logs/zap/entry_encoder_slow.go
index 77bf275d93d242ac9ae38f04efe937fa986e5733..8c3f217d126c8ace9983c8de3c57ed11f16235f3 100644
--- a/logs/zap/entry_encoder_slow.go
+++ b/logs/zap/entry_encoder_slow.go
@@ -4,9 +4,8 @@ import (
 	"fmt"
 	"maps"
 
-	oid "git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/id"
 	"git.perx.ru/perxis/perxis-go/logs"
-	"git.perx.ru/perxis/perxis-go/pkg/id"
 	"go.uber.org/zap/zapcore"
 )
 
@@ -51,8 +50,8 @@ func (enc *entryEncoderSlow) EncodeEntry(entry zapcore.Entry, fields []zapcore.F
 	ent.Category, _ = clone.Fields["category"].(string)
 	ent.Component, _ = clone.Fields["component"].(string)
 	ent.Event, _ = clone.Fields["event"].(string)
-	ent.ObjectID, _ = clone.Fields["object"].(*oid.ObjectId)
-	ent.CallerID, _ = clone.Fields["caller"].(*oid.ObjectId)
+	ent.ObjectID, _ = clone.Fields["object"].(*id.ObjectId)
+	ent.CallerID, _ = clone.Fields["caller"].(*id.ObjectId)
 	ent.Attr = clone.Fields["attr"]
 
 	if err, _ := clone.Fields["error"].(error); err != nil {
diff --git a/logs/zap/entry_encoder_test.go b/logs/zap/entry_encoder_test.go
index c83656708d9aba2e4075acce9ad2d28b88dcbb39..13788bda5cea5718de6b33db1c7c251b5f6d1429 100644
--- a/logs/zap/entry_encoder_test.go
+++ b/logs/zap/entry_encoder_test.go
@@ -1,6 +1,7 @@
 package zap
 
 import (
+	"context"
 	"fmt"
 	"testing"
 
@@ -35,7 +36,7 @@ func TestEntryEncoder_EncodeEntry(t *testing.T) {
 					logzap.Component("Items.Service"),
 					logzap.Event("Items.Create"),
 					logzap.Object("/spaces/WPNN/envs/9VGP/cols/GxNv/items/W0fl"),
-					logzap.Caller("/users/PHVz"),
+					logzap.Caller(context.TODO(), logzap.WithObject("/users/PHVz")),
 					logzap.Attr("any"),
 					logzap.Tags("tag1", "tag2", "tag3"),
 				},
@@ -70,7 +71,7 @@ func BenchmarkEntryEncoderSimple(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
@@ -84,7 +85,7 @@ func BenchmarkEntryEncoderUnknownFields(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
@@ -102,7 +103,7 @@ func BenchmarkEntryEncoderV2Simple(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
@@ -116,7 +117,7 @@ func BenchmarkEntryEncoderV2UnknownFields(b *testing.B) {
 	fields := []zapcore.Field{
 		logzap.Event(items.EventCreate),
 		logzap.Object(items.NewItem("WPNN", "9VGP", "GxNv", "W0fl", nil, nil)),
-		logzap.Caller("/system"),
+		logzap.Caller(context.TODO(), logzap.WithObject("/system")),
 		logzap.Tags("tag1", "tag2", "tag3"),
 	}
 
diff --git a/logs/zap/example_test.go b/logs/zap/example_test.go
index 7e27fa307fcabe62ad45b6b2f465fdea1b9a2d21..dc182fb0fb764b9b7ca3a232678564c8878a72b0 100644
--- a/logs/zap/example_test.go
+++ b/logs/zap/example_test.go
@@ -77,7 +77,7 @@ func TestExample(t *testing.T) {
 		logger.Info("Item created",
 			logzap.Event(items.EventCreate),
 			logzap.Object(item),
-			logzap.CallerFromContext(ctx, item.SpaceID),
+			logzap.Caller(ctx, logzap.WithSpace(item.SpaceID)),
 			logzap.Tags("tag1", "tag2", "tag3"),
 		)
 
@@ -85,7 +85,7 @@ func TestExample(t *testing.T) {
 		logger.Warn("Item updated",
 			logzap.Event(items.EventUpdate),
 			logzap.Object(item),
-			logzap.CallerFromContext(ctx, item.SpaceID),
+			logzap.Caller(ctx, logzap.WithSpace(item.SpaceID)),
 			logzap.Attr(map[string]map[string]any{"title": {"old": "old title", "new": "new title"}}),
 		)
 	}
diff --git a/perxis-proto b/perxis-proto
index f10336dc4a4f58111c12dd95afec82be18388803..b2ac7b057b6e815506d40f9ae41ad16733859d7b 160000
--- a/perxis-proto
+++ b/perxis-proto
@@ -1 +1 @@
-Subproject commit f10336dc4a4f58111c12dd95afec82be18388803
+Subproject commit b2ac7b057b6e815506d40f9ae41ad16733859d7b
diff --git a/pkg/account/client.go b/pkg/account/client.go
index 112a71f67fe130bb57c6483ad4793565bca8d0bd..5349ed2917ed691f6661930ea0ffce72a79ab814 100644
--- a/pkg/account/client.go
+++ b/pkg/account/client.go
@@ -34,10 +34,10 @@ func NewClient(conn *grpc.ClientConn, opts ...Option) *Account {
 		c.logger = zap.NewNop()
 	}
 
-	client.Members = membersTransport.NewGRPCClient(conn, "", c.clientOptions...)
-	client.Organizations = organizationsTransport.NewGRPCClient(conn, "", c.clientOptions...)
-	client.Users = usersTransport.NewGRPCClient(conn, "", c.clientOptions...)
-	client.MembersObserver = membersObserverTransport.NewGRPCClient(conn, "", c.clientOptions...)
+	client.Members = membersTransport.NewClient(conn, c.clientOptions...)
+	client.Organizations = organizationsTransport.NewClient(conn, c.clientOptions...)
+	client.Users = usersTransport.NewClient(conn, c.clientOptions...)
+	client.MembersObserver = membersObserverTransport.NewClient(conn, c.clientOptions...)
 
 	if !c.noCache {
 		client = WithCaching(client, DefaultCacheSize, DefaultCacheTTL)
diff --git a/pkg/account/config.go b/pkg/account/config.go
index cfa3f088e56829ad3917e274f6e59938fa0098d7..b86eb543931df7d52a8e19ab8db31b59e1db4b10 100644
--- a/pkg/account/config.go
+++ b/pkg/account/config.go
@@ -9,7 +9,6 @@ type config struct {
 	noCache       bool
 	noLog         bool
 	accessLog     bool
-	debug         bool
 	clientOptions []kitgrpc.ClientOption // todo: можно заменить на grpc-интерсепторы при соединении и избавиться здесь от go-kit
 	logger        *zap.Logger
 }
diff --git a/pkg/content/versions/transport/client.microgen.go b/pkg/account/versions/transport/client.go
similarity index 62%
rename from pkg/content/versions/transport/client.microgen.go
rename to pkg/account/versions/transport/client.go
index ec9a69655fab437a4146b6cc61973197f415ff2c..0713d94abcaca463a18d5e129314c0b8769a3ba4 100644
--- a/pkg/content/versions/transport/client.microgen.go
+++ b/pkg/account/versions/transport/client.go
@@ -4,20 +4,14 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	version "git.perx.ru/perxis/perxis-go/pkg/version"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Get(arg0 context.Context) (res0 *version.Version, res1 error) {
 	request := GetRequest{}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Version, res1
diff --git a/pkg/account/versions/transport/grpc/client.go b/pkg/account/versions/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..e5a94578ed037a87fbdfb5e7fdb324d401627f62
--- /dev/null
+++ b/pkg/account/versions/transport/grpc/client.go
@@ -0,0 +1,17 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/account/versions/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		GetEndpoint: grpcerr.ClientMiddleware(c.GetEndpoint),
+	}
+}
diff --git a/pkg/account/versions/transport/grpc/server.go b/pkg/account/versions/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..960a9d0fba0b36e8658b84a692170714b42e281f
--- /dev/null
+++ b/pkg/account/versions/transport/grpc/server.go
@@ -0,0 +1,17 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/account/versions/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	versions "git.perx.ru/perxis/perxis-go/pkg/version"
+	pb "git.perx.ru/perxis/perxis-go/proto/versions/account"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc versions.Versions, opts ...grpckit.ServerOption) pb.VersionsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		GetEndpoint: grpcerr.ServerMiddleware(eps.GetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/auth/client.go b/pkg/auth/client.go
index 2bb3a12f34c05dc7796165ee9f8e87c66cd91ef2..76645202645c3cd4bea87956329d88c2c6759cd3 100644
--- a/pkg/auth/client.go
+++ b/pkg/auth/client.go
@@ -50,7 +50,7 @@ func (c *ClientPrincipal) Format(f fmt.State, verb rune) {
 		id = c.client.ID
 	}
 
-	f.Write([]byte(fmt.Sprintf("ClientPrincipal{ID: '%s', Identity: {%s}}", id, identity)))
+	_, _ = f.Write([]byte(fmt.Sprintf("ClientPrincipal{ID: '%s', Identity: {%s}}", id, identity)))
 }
 
 func (c *ClientPrincipal) GetID(ctx context.Context) string {
diff --git a/pkg/auth/grpc.go b/pkg/auth/grpc.go
index e6cd00929f13a0cf702f1dee8bdb9dcd33768ab6..a947a33bab75da4c55ca9575e15d915fbc480b67 100644
--- a/pkg/auth/grpc.go
+++ b/pkg/auth/grpc.go
@@ -74,7 +74,7 @@ func ContextToGRPC() kitgrpc.ClientRequestFunc {
 	}
 }
 
-// PrincipalServerInterceptor - grpc-интерсептор, который используется для получения данных принципала из grpc-метаданы и добавления в контекст ''. В случае, если
+// PrincipalServerInterceptor - grpc-интерсептор, который используется для получения данных принципала из grpc-метаданы и добавления в контекст ”. В случае, если
 // сервис не использует проверку прав 'Principal' к системе, в параметрах передается пустой объект '&PrincipalFactory{}'
 func PrincipalServerInterceptor(factory *PrincipalFactory) grpc.UnaryServerInterceptor {
 	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
@@ -98,6 +98,13 @@ func PrincipalClientInterceptor() grpc.UnaryClientInterceptor {
 	}
 }
 
+func AddAccessInterceptor(id string) grpc.UnaryClientInterceptor {
+	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+		ctx = metadata.AppendToOutgoingContext(ctx, AccessMetadata, id)
+		return invoker(ctx, method, req, reply, cc, opts...)
+	}
+}
+
 func AddAuthorizationInterceptor(auth string) grpc.UnaryClientInterceptor {
 	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
 		ctx = metadata.AppendToOutgoingContext(ctx, "authorization", auth)
@@ -124,6 +131,15 @@ func WithOAuth2Credentials(ctx context.Context, tokenURL, clientID, clientSecret
 
 // WithTLSCredentials возвращает опции для создания grpc-соединения с TLS-сертификатами
 func WithTLSCredentials(ctx context.Context, cert, cacert, key []byte) (grpc.DialOption, error) {
+	creds, err := TLSCredentials(ctx, cert, cacert, key)
+	if err != nil {
+		return nil, err
+	}
+	return grpc.WithTransportCredentials(creds), nil
+}
+
+// TLSCredentials возвращает TransportCredentials для создания grpc-соединения с TLS-сертификатами
+func TLSCredentials(ctx context.Context, cert, cacert, key []byte) (credentials.TransportCredentials, error) {
 	certPool := x509.NewCertPool()
 	if !certPool.AppendCertsFromPEM(cacert) {
 		return nil, errors.New("CA certificate not loaded")
@@ -132,5 +148,5 @@ func WithTLSCredentials(ctx context.Context, cert, cacert, key []byte) (grpc.Dia
 	if err != nil {
 		return nil, err
 	}
-	return grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{clientCert}, RootCAs: certPool})), nil
+	return credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{clientCert}, RootCAs: certPool}), nil
 }
diff --git a/pkg/auth/user.go b/pkg/auth/user.go
index 0bd08c62b4a2ae15b88808a8ad8025020d2f4343..33f7fb4c801f109ff7cad4811cc1d42d93a6ea8d 100644
--- a/pkg/auth/user.go
+++ b/pkg/auth/user.go
@@ -37,7 +37,7 @@ type UserPrincipal struct {
 }
 
 func (u UserPrincipal) Format(f fmt.State, verb rune) {
-	f.Write([]byte(fmt.Sprintf("UserPrincipal{ID: '%s', Identity: '%s'}", u.id, u.identity)))
+	_, _ = f.Write([]byte(fmt.Sprintf("UserPrincipal{ID: '%s', Identity: '%s'}", u.id, u.identity)))
 }
 
 func (u *UserPrincipal) GetID(ctx context.Context) string {
diff --git a/pkg/cli/gracefull.go b/pkg/cli/gracefull.go
index 8fd64a3d612d2e99e3d3d255a29e4dac00ebd8ea..64ecc3713a6e06de490d22a80c3490f3008110ff 100644
--- a/pkg/cli/gracefull.go
+++ b/pkg/cli/gracefull.go
@@ -14,23 +14,20 @@ func WaitForQuit(logger *zap.Logger, finailizer func()) {
 	signal.Notify(sigc, syscall.SIGTERM, os.Interrupt)
 	var signalsReceived uint
 	go func() {
-		for {
-			select {
-			case s := <-sigc:
-				logger.Info("Signal received. Exiting", zap.String("Signal", s.String()))
-				signalsReceived++
+		for s := range sigc {
+			logger.Info("Signal received. Exiting", zap.String("Signal", s.String()))
+			signalsReceived++
 
-				if signalsReceived < 2 {
-					// After first Ctrl+C start quitting the worker gracefully
-					go func() {
-						finailizer()
-						close(donec)
-					}()
-				} else {
-					// Abort the program when user hits Ctrl+C second time in a row
-					logger.Info("Force exit")
+			if signalsReceived < 2 {
+				// After first Ctrl+C start quitting the worker gracefully
+				go func() {
+					finailizer()
 					close(donec)
-				}
+				}()
+			} else {
+				// Abort the program when user hits Ctrl+C second time in a row
+				logger.Info("Force exit")
+				close(donec)
 			}
 		}
 	}()
diff --git a/pkg/clients/client.go b/pkg/clients/client.go
index f38b5acc9b442be39139523bcd316947d5d54fe7..6c4c6735b8ec91f226ba5306b2649252390ffc67 100644
--- a/pkg/clients/client.go
+++ b/pkg/clients/client.go
@@ -45,6 +45,11 @@ type TLS struct {
 	Key     string `json:"key,omitempty"`
 }
 
+// GetID возвращает идентификатор клиента
+func (c Client) GetID() string {
+	return c.ID
+}
+
 func (c *Client) SetDisabled(b bool) *Client {
 	c.Disabled = &b
 	return c
diff --git a/pkg/clients/events.go b/pkg/clients/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..98ece5ff8a62e11e14b3c23e873feafe3e1fcc38
--- /dev/null
+++ b/pkg/clients/events.go
@@ -0,0 +1,9 @@
+package clients
+
+const (
+	EventCreate  = "clients.create"
+	EventUpdate  = "clients.update"
+	EventDelete  = "clients.delete"
+	EventEnable  = "clients.enable"
+	EventDisable = "clients.disable"
+)
diff --git a/pkg/clients/middleware/caching_middleware.go b/pkg/clients/middleware/caching_middleware.go
index 08af3a47a7cd3f23b8ae07e4050f9e18f1be1ee3..0dd4f15b4e24affb13562c57a1d69b0e3adce33d 100644
--- a/pkg/clients/middleware/caching_middleware.go
+++ b/pkg/clients/middleware/caching_middleware.go
@@ -6,6 +6,7 @@ import (
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
 	service "git.perx.ru/perxis/perxis-go/pkg/clients"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 )
 
 func makeKey(ss ...string) string {
@@ -30,7 +31,7 @@ func (m cachingMiddleware) Create(ctx context.Context, client *service.Client) (
 
 	cl, err = m.next.Create(ctx, client)
 	if err == nil {
-		m.cache.Remove(cl.SpaceID)
+		_ = m.cache.Remove(cl.SpaceID)
 	}
 	return cl, err
 }
@@ -40,16 +41,17 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId string, id string) (
 	key := makeKey(spaceId, id)
 	value, e := m.cache.Get(key)
 	if e == nil {
-		return value.(*service.Client), err
+		return value.(*service.Client).Clone(), nil
 	}
 	cl, err = m.next.Get(ctx, spaceId, id)
 	if err == nil {
-		m.cache.Set(key, cl)
+		_ = m.cache.Set(key, cl)
 		for _, key := range keysFromIdentities(spaceId, cl) {
-			m.cache.Set(key, cl)
+			_ = m.cache.Set(key, cl)
 		}
+		return cl.Clone(), nil
 	}
-	return cl, err
+	return nil, err
 }
 
 func (m cachingMiddleware) GetBy(ctx context.Context, spaceId string, params *service.GetByParams) (cl *service.Client, err error) {
@@ -60,29 +62,31 @@ func (m cachingMiddleware) GetBy(ctx context.Context, spaceId string, params *se
 	key := getIdentKey(spaceId, params)
 	value, e := m.cache.Get(key)
 	if e == nil {
-		return value.(*service.Client), err
+		return value.(*service.Client).Clone(), nil
 	}
 	cl, err = m.next.GetBy(ctx, spaceId, params)
 	if err == nil {
-		m.cache.Set(makeKey(spaceId, cl.ID), cl)
+		_ = m.cache.Set(makeKey(spaceId, cl.ID), cl)
 		for _, key := range keysFromIdentities(spaceId, cl) {
-			m.cache.Set(key, cl)
+			_ = m.cache.Set(key, cl)
 		}
+		return cl.Clone(), nil
 	}
-	return cl, err
+	return nil, err
 }
 
 func (m cachingMiddleware) List(ctx context.Context, spaceId string) (clients []*service.Client, err error) {
 
 	value, e := m.cache.Get(spaceId)
 	if e == nil {
-		return value.([]*service.Client), err
+		return data.CloneSlice(value.([]*service.Client)), nil
 	}
 	clients, err = m.next.List(ctx, spaceId)
 	if err == nil {
-		m.cache.Set(spaceId, clients)
+		_ = m.cache.Set(spaceId, clients)
+		return data.CloneSlice(clients), nil
 	}
-	return clients, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Update(ctx context.Context, client *service.Client) (err error) {
@@ -90,13 +94,13 @@ func (m cachingMiddleware) Update(ctx context.Context, client *service.Client) (
 	err = m.next.Update(ctx, client)
 
 	if err == nil {
-		m.cache.Remove(client.SpaceID)
+		_ = m.cache.Remove(client.SpaceID)
 		value, e := m.cache.Get(makeKey(client.SpaceID, client.ID))
 		if e == nil {
 			client := value.(*service.Client)
-			m.cache.Remove(makeKey(client.SpaceID, client.ID))
+			_ = m.cache.Remove(makeKey(client.SpaceID, client.ID))
 			for _, key := range keysFromIdentities(client.SpaceID, client) {
-				m.cache.Remove(key)
+				_ = m.cache.Remove(key)
 			}
 		}
 	}
@@ -110,12 +114,12 @@ func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, id string
 		value, e := m.cache.Get(makeKey(spaceId, id))
 		if e == nil {
 			client := value.(*service.Client)
-			m.cache.Remove(makeKey(client.SpaceID, client.ID))
+			_ = m.cache.Remove(makeKey(client.SpaceID, client.ID))
 			for _, key := range keysFromIdentities(client.SpaceID, client) {
-				m.cache.Remove(key)
+				_ = m.cache.Remove(key)
 			}
 		}
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
@@ -127,12 +131,12 @@ func (m cachingMiddleware) Enable(ctx context.Context, spaceId string, id string
 		value, e := m.cache.Get(makeKey(spaceId, id))
 		if e == nil {
 			client := value.(*service.Client)
-			m.cache.Remove(makeKey(client.SpaceID, client.ID))
+			_ = m.cache.Remove(makeKey(client.SpaceID, client.ID))
 			for _, key := range keysFromIdentities(client.SpaceID, client) {
-				m.cache.Remove(key)
+				_ = m.cache.Remove(key)
 			}
 		}
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
diff --git a/pkg/clients/middleware/caching_middleware_test.go b/pkg/clients/middleware/caching_middleware_test.go
index 2a90c1db2c322ca9509cb2bf9bdd63b252e67c68..821d2cb29579029f10e1726291212b6ccaeaceae 100644
--- a/pkg/clients/middleware/caching_middleware_test.go
+++ b/pkg/clients/middleware/caching_middleware_test.go
@@ -40,11 +40,13 @@ func TestClientsCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, cltID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша, после повторного запроса.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша, после повторного запроса.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
 		require.NoError(t, err)
-		assert.Same(t, v2, v3, "Ожидается получение объекта из кэша при запросе по ClientID.")
+		assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша при запросе по ClientID.")
+		assert.NotSame(t, v2, v3)
 
 		cs.AssertExpectations(t)
 	})
@@ -61,11 +63,13 @@ func TestClientsCache(t *testing.T) {
 
 		v2, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша, после повторного запроса.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша, после повторного запроса.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.Get(ctx, spaceID, cltID)
 		require.NoError(t, err)
-		assert.Same(t, v2, v3, "Ожидается получение объекта из кэша, после запроса Get.")
+		assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша, после запроса Get.")
+		assert.NotSame(t, v2, v3)
 
 		cs.AssertExpectations(t)
 	})
@@ -82,7 +86,8 @@ func TestClientsCache(t *testing.T) {
 
 		vl2, err := svc.List(ctx, spaceID)
 		require.NoError(t, err)
-		assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+		assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+		assert.NotSame(t, vl1[0], vl2[0])
 
 		cs.AssertExpectations(t)
 	})
@@ -102,11 +107,13 @@ func TestClientsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по ClientID.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша по ClientID.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
@@ -114,7 +121,8 @@ func TestClientsCache(t *testing.T) {
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 			assert.Len(t, vl2, 1)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			cs.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 
@@ -126,16 +134,17 @@ func TestClientsCache(t *testing.T) {
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается получение объектов из кэша, после повторного запроса.")
 
 			v4, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v4, "Ожидает что после обновления объект был удален из кэша и будет запрошен заново из сервиса.")
+			assert.NotEqual(t, v2, v4, "Ожидает что после обновления объект был удален из кэша и будет запрошен заново из сервиса.")
 
 			v5, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v5)
-			assert.Same(t, v4, v5, "Ожидается что после обновления объект был удален из кеша и после запроса Get в кеш попал объект запрошенный заново из сервиса.")
+			assert.NotEqual(t, v3, v5)
+			assert.Equal(t, v4, v5, "Ожидается что после обновления объект был удален из кеша и после запроса Get в кеш попал объект запрошенный заново из сервиса.")
+			assert.NotSame(t, v4, v5)
 
 			cs.AssertExpectations(t)
 		})
@@ -153,7 +162,8 @@ func TestClientsCache(t *testing.T) {
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 			assert.Len(t, vl2, 1)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			cs.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 
@@ -164,7 +174,7 @@ func TestClientsCache(t *testing.T) {
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается получение объектов из кэша, после повторного запроса.")
 
 			cs.AssertExpectations(t)
 		})
@@ -182,18 +192,21 @@ func TestClientsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по ClientID.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша по ClientID.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			cs.On("Delete", mock.Anything, spaceID, cltID).Return(nil).Once()
 
@@ -231,7 +244,8 @@ func TestClientsCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			cs.On("Delete", mock.Anything, spaceID, cltID).Return(nil).Once()
 
@@ -259,7 +273,8 @@ func TestClientsCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 			assert.Len(t, vl2, 1, "Ожидается получение объектов из кэша.")
 
 			cs.On("Create", mock.Anything, mock.Anything).Return(&clients.Client{ID: "cltID2", SpaceID: spaceID, Name: "client_2"}, nil).Once()
@@ -290,18 +305,21 @@ func TestClientsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по ClientID.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша по ClientID.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			cs.On("Enable", mock.Anything, spaceID, cltID, tr).Return(nil).Once()
 
@@ -314,14 +332,15 @@ func TestClientsCache(t *testing.T) {
 
 			v4, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v4, "Ожидается что после активации объект был удален из кэша и запрошен у сервиса.")
+			assert.NotEqual(t, v2, v4, "Ожидается что после активации объект был удален из кэша и запрошен у сервиса.")
 
 			v5, err := svc.GetBy(ctx, spaceID, &clients.GetByParams{OAuthClientID: clientID})
-			assert.NotSame(t, v3, v5, "Ожидается что после активации объект был удален из кеша и после запроса Get в кеш попал объект запрошенный заново из сервиса.")
+			require.NoError(t, err)
+			assert.NotEqual(t, v3, v5, "Ожидается что после активации объект был удален из кеша и после запроса Get в кеш попал объект запрошенный заново из сервиса.")
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что после активации объекта, кеш будет очищен и объекты будут запрошены заново из сервиса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается что после активации объекта, кеш будет очищен и объекты будут запрошены заново из сервиса.")
 
 			cs.AssertExpectations(t)
 		})
@@ -339,7 +358,8 @@ func TestClientsCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша, после повторного запроса.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			cs.On("Enable", mock.Anything, spaceID, cltID, tr).Return(nil).Once()
 
@@ -351,7 +371,7 @@ func TestClientsCache(t *testing.T) {
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что после активации объекта, кеш будет очищен и объекты будут запрошены заново из сервиса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается что после активации объекта, кеш будет очищен и объекты будут запрошены заново из сервиса.")
 
 			cs.AssertExpectations(t)
 		})
@@ -368,13 +388,16 @@ func TestClientsCache(t *testing.T) {
 			require.NoError(t, err)
 			v2, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша после повторного запроса.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша после повторного запроса.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 
 			v3, err := svc.Get(ctx, spaceID, cltID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается что элемент был удален из кэша по истечению ttl и будет запрошен заново из сервиса.")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			cs.AssertExpectations(t)
 		})
diff --git a/pkg/clients/middleware/logging_middleware.go b/pkg/clients/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..8cc5e384fa8fcadee5dc86c84444dead233bcb81
--- /dev/null
+++ b/pkg/clients/middleware/logging_middleware.go
@@ -0,0 +1,145 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   clients.Clients
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next clients.Clients) clients.Clients {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Clients")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, client *clients.Client) (created *clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx, logzap.WithSpace(client.SpaceID)),
+		logzap.Event(clients.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, client)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(client))
+		return
+	}
+
+	logger.Info("Client created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId, clientId string) (client *clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewClientId(spaceId, clientId)),
+	)
+
+	client, err = m.next.Get(ctx, spaceId, clientId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return client, err
+}
+
+func (m *loggingMiddleware) GetBy(ctx context.Context, spaceId string, params *clients.GetByParams) (client *clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	client, err = m.next.GetBy(ctx, spaceId, params)
+	if err != nil {
+		logger.Error("Failed to get by", zap.Error(err))
+		return
+	}
+
+	return client, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (clients []*clients.Client, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	clients, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return clients, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, client *clients.Client) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(clients.EventUpdate),
+		logzap.Object(client),
+	)
+
+	err = m.next.Update(ctx, client)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Client updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId, clientId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(clients.EventDelete),
+		logzap.Object(id.NewClientId(spaceId, clientId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, clientId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Client deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Enable(ctx context.Context, spaceId, clientId string, enable bool) (err error) {
+	event := clients.EventDisable
+	logMsg := "disable"
+
+	if enable {
+		event = clients.EventEnable
+		logMsg = "enable"
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(event),
+		logzap.Object(id.NewClientId(spaceId, clientId)),
+	)
+
+	err = m.next.Enable(ctx, spaceId, clientId, enable)
+	if err != nil {
+		logger.Error("Failed to "+logMsg, zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Client "+logMsg+"d", logzap.Channels(logzap.Userlog))
+	return err
+}
diff --git a/pkg/clients/middleware/middleware.go b/pkg/clients/middleware/middleware.go
index 0c72c1660e15f147a773e2da5e2f31b469b90260..945ae1f2afde7274140b6cbd3c84fff260d0b1cb 100644
--- a/pkg/clients/middleware/middleware.go
+++ b/pkg/clients/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s clients.Clients, logger *zap.Logger, log_access bool) clients.Cli
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/clients/transport/client.microgen.go b/pkg/clients/transport/client.go
similarity index 66%
rename from pkg/clients/transport/client.microgen.go
rename to pkg/clients/transport/client.go
index 633855af4d1f7fe74260ec7b4ece23db6063ebfb..008f60b019deac5e9e86aff7a6687a3c3fe8361a 100644
--- a/pkg/clients/transport/client.microgen.go
+++ b/pkg/clients/transport/client.go
@@ -4,19 +4,13 @@ package transport
 
 import (
 	"context"
-	"errors"
 	clients "git.perx.ru/perxis/perxis-go/pkg/clients"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *clients.Client) (res0 *clients.Client, res1 error) {
 	request := CreateRequest{Client: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
@@ -29,9 +23,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Client, res1
@@ -44,9 +35,6 @@ func (set EndpointsSet) GetBy(arg0 context.Context, arg1 string, arg2 *clients.G
 	}
 	response, res1 := set.GetByEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetByResponse).Client, res1
@@ -56,9 +44,6 @@ func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*clients
 	request := ListRequest{SpaceId: arg1}
 	response, res1 := set.ListEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListResponse).Clients, res1
@@ -68,9 +53,6 @@ func (set EndpointsSet) Update(arg0 context.Context, arg1 *clients.Client) (res0
 	request := UpdateRequest{Client: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -83,9 +65,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (
 	}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -99,9 +78,6 @@ func (set EndpointsSet) Enable(arg0 context.Context, arg1 string, arg2 string, a
 	}
 	_, res0 = set.EnableEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/clients/transport/grpc/client.go b/pkg/clients/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..fe6ba6fc8a14622b002b7f96b73f124c2ff2d5b2
--- /dev/null
+++ b/pkg/clients/transport/grpc/client.go
@@ -0,0 +1,23 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/clients/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ClientMiddleware(c.CreateEndpoint),
+		GetEndpoint:    grpcerr.ClientMiddleware(c.GetEndpoint),
+		GetByEndpoint:  grpcerr.ClientMiddleware(c.GetByEndpoint),
+		ListEndpoint:   grpcerr.ClientMiddleware(c.ListEndpoint),
+		UpdateEndpoint: grpcerr.ClientMiddleware(c.UpdateEndpoint),
+		DeleteEndpoint: grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		EnableEndpoint: grpcerr.ClientMiddleware(c.EnableEndpoint),
+	}
+}
diff --git a/pkg/clients/transport/grpc/server.go b/pkg/clients/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..ee019551141c4e3c6af7d4805cb6924a19e4d010
--- /dev/null
+++ b/pkg/clients/transport/grpc/server.go
@@ -0,0 +1,23 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	"git.perx.ru/perxis/perxis-go/pkg/clients/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	pb "git.perx.ru/perxis/perxis-go/proto/clients"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc clients.Clients, opts ...grpckit.ServerOption) pb.ClientsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		GetEndpoint:    grpcerr.ServerMiddleware(eps.GetEndpoint),
+		GetByEndpoint:  grpcerr.ServerMiddleware(eps.GetByEndpoint),
+		ListEndpoint:   grpcerr.ServerMiddleware(eps.ListEndpoint),
+		UpdateEndpoint: grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+		DeleteEndpoint: grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		EnableEndpoint: grpcerr.ServerMiddleware(eps.EnableEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/collaborators/collaborator.go b/pkg/collaborators/collaborator.go
index 701d8e85b578dafc2036c281c204720080412ac5..5fe1b481c231568af13b9c9e827eede723312576 100644
--- a/pkg/collaborators/collaborator.go
+++ b/pkg/collaborators/collaborator.go
@@ -5,3 +5,11 @@ type Collaborator struct {
 	Subject string `bson:"subject"`
 	Role    string `bson:"role"`
 }
+
+func (c Collaborator) Clone() *Collaborator {
+	return &Collaborator{
+		SpaceID: c.SpaceID,
+		Subject: c.Subject,
+		Role:    c.Role,
+	}
+}
diff --git a/pkg/collaborators/events.go b/pkg/collaborators/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f4a8b7c2493f2b65a690f675f5a27a7eb344533
--- /dev/null
+++ b/pkg/collaborators/events.go
@@ -0,0 +1,6 @@
+package collaborators
+
+const (
+	EventSet    = "collaborators.set"
+	EventRemove = "collaborators.remove"
+)
diff --git a/pkg/collaborators/middleware/caching_middleware.go b/pkg/collaborators/middleware/caching_middleware.go
index 7533ae551ed47a421d3fe8ed643ce679e07fddc2..7faeb638b0ca2bb673de4852c21e75b7f1f9afc7 100644
--- a/pkg/collaborators/middleware/caching_middleware.go
+++ b/pkg/collaborators/middleware/caching_middleware.go
@@ -6,6 +6,7 @@ import (
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
 	service "git.perx.ru/perxis/perxis-go/pkg/collaborators"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 )
 
 func makeKey(ss ...string) string {
@@ -30,8 +31,8 @@ func (m cachingMiddleware) Set(ctx context.Context, spaceId, subject, role strin
 
 	err = m.next.Set(ctx, spaceId, subject, role)
 	if err == nil {
-		m.cache.Remove(spaceId)
-		m.cache.Remove(subject)
+		_ = m.cache.Remove(spaceId)
+		_ = m.cache.Remove(subject)
 	}
 	return err
 }
@@ -45,7 +46,7 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId, subject string) (ro
 	}
 	role, err = m.next.Get(ctx, spaceId, subject)
 	if err == nil {
-		m.cache.Set(key, role)
+		_ = m.cache.Set(key, role)
 	}
 	return role, err
 }
@@ -54,9 +55,9 @@ func (m cachingMiddleware) Remove(ctx context.Context, spaceId, subject string)
 
 	err = m.next.Remove(ctx, spaceId, subject)
 	if err == nil {
-		m.cache.Remove(makeKey(spaceId, subject))
-		m.cache.Remove(spaceId)
-		m.cache.Remove(subject)
+		_ = m.cache.Remove(makeKey(spaceId, subject))
+		_ = m.cache.Remove(spaceId)
+		_ = m.cache.Remove(subject)
 	}
 	return err
 }
@@ -65,24 +66,26 @@ func (m cachingMiddleware) ListCollaborators(ctx context.Context, spaceId string
 
 	value, e := m.cache.Get(spaceId)
 	if e == nil {
-		return value.([]*service.Collaborator), err
+		return data.CloneSlice(value.([]*service.Collaborator)), nil
 	}
 	collaborators, err = m.next.ListCollaborators(ctx, spaceId)
 	if err == nil {
-		m.cache.Set(spaceId, collaborators)
+		_ = m.cache.Set(spaceId, collaborators)
+		return data.CloneSlice(collaborators), nil
 	}
-	return collaborators, err
+	return nil, err
 }
 
 func (m cachingMiddleware) ListSpaces(ctx context.Context, subject string) (collaborators []*service.Collaborator, err error) {
 
 	value, e := m.cache.Get(subject)
 	if e == nil {
-		return value.([]*service.Collaborator), err
+		return data.CloneSlice(value.([]*service.Collaborator)), nil
 	}
 	collaborators, err = m.next.ListSpaces(ctx, subject)
 	if err == nil {
-		m.cache.Set(subject, collaborators)
+		_ = m.cache.Set(subject, collaborators)
+		return data.CloneSlice(collaborators), nil
 	}
-	return collaborators, err
+	return nil, err
 }
diff --git a/pkg/collaborators/middleware/caching_middleware_test.go b/pkg/collaborators/middleware/caching_middleware_test.go
index ea2ccaddb2621c306e317d6d873a455489a6a2a6..6b96d0a8ff65dcc56d3139e2b99eac28720bb364 100644
--- a/pkg/collaborators/middleware/caching_middleware_test.go
+++ b/pkg/collaborators/middleware/caching_middleware_test.go
@@ -56,7 +56,8 @@ func TestCollaboratorsCache(t *testing.T) {
 		require.NoError(t, err)
 		v2, err := svc.ListCollaborators(ctx, spaceID)
 		require.NoError(t, err)
-		assert.Same(t, v1[0], v2[0], "Ожидается получение объектов из кэша при повторном запросе.")
+		assert.Equal(t, v1[0], v2[0], "Ожидается получение объектов из кэша при повторном запросе.")
+		assert.NotSame(t, v1[0], v2[0])
 
 		cs.AssertExpectations(t)
 	})
@@ -72,7 +73,8 @@ func TestCollaboratorsCache(t *testing.T) {
 		require.NoError(t, err)
 		v2, err := svc.ListSpaces(ctx, userID)
 		require.NoError(t, err)
-		assert.Same(t, v1[0], v2[0], "Ожидается получение объектов из кэша при повторном запросе.")
+		assert.Equal(t, v1[0], v2[0], "Ожидается получение объектов из кэша при повторном запросе.")
+		assert.NotSame(t, v1[0], v2[0])
 
 		cs.AssertExpectations(t)
 	})
@@ -98,13 +100,15 @@ func TestCollaboratorsCache(t *testing.T) {
 			require.NoError(t, err)
 			lc2, err := svc.ListCollaborators(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, lc1[0], lc2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, lc1[0], lc2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, lc1[0], lc2[0])
 
 			ls1, err := svc.ListSpaces(ctx, userID)
 			require.NoError(t, err)
 			ls2, err := svc.ListSpaces(ctx, userID)
 			require.NoError(t, err)
-			assert.Same(t, ls1[0], ls2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, ls1[0], ls2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, ls1[0], ls2[0])
 
 			cs.On("Remove", mock.Anything, spaceID, userID).Return(nil).Once()
 
@@ -113,6 +117,7 @@ func TestCollaboratorsCache(t *testing.T) {
 			cs.On("ListSpaces", mock.Anything, userID).Return(nil, errNotFound).Once()
 
 			err = svc.Remove(ctx, spaceID, userID)
+			require.NoError(t, err)
 
 			rl, err = svc.Get(ctx, spaceID, userID)
 			require.Error(t, err)
@@ -152,13 +157,15 @@ func TestCollaboratorsCache(t *testing.T) {
 			require.NoError(t, err)
 			lc2, err := svc.ListCollaborators(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, lc1[0], lc2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, lc1[0], lc2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, lc1[0], lc2[0])
 
 			ls1, err := svc.ListSpaces(ctx, userID)
 			require.NoError(t, err)
 			ls2, err := svc.ListSpaces(ctx, userID)
 			require.NoError(t, err)
-			assert.Same(t, ls1[0], ls2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, ls1[0], ls2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, ls1[0], ls2[0])
 
 			cs.On("Remove", mock.Anything, spaceID, userID).Return(nil).Once()
 
@@ -167,6 +174,7 @@ func TestCollaboratorsCache(t *testing.T) {
 			cs.On("ListSpaces", mock.Anything, userID).Return(nil, errNotFound).Once()
 
 			err = svc.Remove(ctx, spaceID, userID)
+			require.NoError(t, err)
 
 			rl, err = svc.Get(ctx, spaceID, userID)
 			require.Error(t, err)
diff --git a/pkg/collaborators/middleware/logging_middleware.go b/pkg/collaborators/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..645750f0ff7244275a70eaac24e98977d9702e0d
--- /dev/null
+++ b/pkg/collaborators/middleware/logging_middleware.go
@@ -0,0 +1,103 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/collaborators"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   collaborators.Collaborators
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next collaborators.Collaborators) collaborators.Collaborators {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Collaborators")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Set(ctx context.Context, spaceId, subject, role string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(collaborators.EventSet),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.Set(ctx, spaceId, subject, role)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to set user '%s' as a collaborator with role '%s'", subject, role), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' assigned to role '%s'", subject, role), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId, subject string) (role string, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	role, err = m.next.Get(ctx, spaceId, subject)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to get role for collaborator %s", subject), zap.Error(err))
+		return
+	}
+
+	return role, err
+}
+
+func (m *loggingMiddleware) Remove(ctx context.Context, spaceId, subject string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(collaborators.EventRemove),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.Remove(ctx, spaceId, subject)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to remove user '%s' from space", subject), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' removed from space", subject), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) ListCollaborators(ctx context.Context, spaceId string) (collaborators []*collaborators.Collaborator, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	collaborators, err = m.next.ListCollaborators(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list collaborators", zap.Error(err))
+		return
+	}
+
+	return collaborators, err
+}
+
+func (m *loggingMiddleware) ListSpaces(ctx context.Context, subject string) (spaces []*collaborators.Collaborator, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, err = m.next.ListSpaces(ctx, subject)
+	if err != nil {
+		logger.Error("Failed to list spaces", zap.Error(err))
+		return
+	}
+
+	return spaces, err
+}
diff --git a/pkg/collaborators/middleware/middleware.go b/pkg/collaborators/middleware/middleware.go
index 28f0bc687c5ee66f2395303efa502149f8394644..a336e089fc00e9984ba4ddb1c9d656a46d98c493 100644
--- a/pkg/collaborators/middleware/middleware.go
+++ b/pkg/collaborators/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s collaborators.Collaborators, logger *zap.Logger, log_access bool)
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/collaborators/transport/client.microgen.go b/pkg/collaborators/transport/client.go
similarity index 67%
rename from pkg/collaborators/transport/client.microgen.go
rename to pkg/collaborators/transport/client.go
index eb0dbbe7bc1ea7bad07220fba5cdc6e3b1c9e503..1e6477a11347742e6cf250ff336d3ca7f89b5ac0 100644
--- a/pkg/collaborators/transport/client.microgen.go
+++ b/pkg/collaborators/transport/client.go
@@ -4,11 +4,8 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	collaborators "git.perx.ru/perxis/perxis-go/pkg/collaborators"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Set(arg0 context.Context, arg1 string, arg2 string, arg3 string) (res0 error) {
@@ -19,9 +16,6 @@ func (set EndpointsSet) Set(arg0 context.Context, arg1 string, arg2 string, arg3
 	}
 	_, res0 = set.SetEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -34,9 +28,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Role, res1
@@ -49,9 +40,6 @@ func (set EndpointsSet) Remove(arg0 context.Context, arg1 string, arg2 string) (
 	}
 	_, res0 = set.RemoveEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -61,9 +49,6 @@ func (set EndpointsSet) ListCollaborators(arg0 context.Context, arg1 string) (re
 	request := ListCollaboratorsRequest{SpaceId: arg1}
 	response, res1 := set.ListCollaboratorsEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListCollaboratorsResponse).Collaborators, res1
@@ -73,9 +58,6 @@ func (set EndpointsSet) ListSpaces(arg0 context.Context, arg1 string) (res0 []*c
 	request := ListSpacesRequest{Subject: arg1}
 	response, res1 := set.ListSpacesEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListSpacesResponse).Spaces, res1
diff --git a/pkg/collaborators/transport/grpc/client.go b/pkg/collaborators/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..ed7c951fe02289ce5d20e4ffec6d473024344591
--- /dev/null
+++ b/pkg/collaborators/transport/grpc/client.go
@@ -0,0 +1,21 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/collaborators/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		GetEndpoint:               grpcerr.ClientMiddleware(c.GetEndpoint),
+		ListSpacesEndpoint:        grpcerr.ClientMiddleware(c.ListSpacesEndpoint),
+		ListCollaboratorsEndpoint: grpcerr.ClientMiddleware(c.ListCollaboratorsEndpoint),
+		RemoveEndpoint:            grpcerr.ClientMiddleware(c.RemoveEndpoint),
+		SetEndpoint:               grpcerr.ClientMiddleware(c.SetEndpoint),
+	}
+}
diff --git a/pkg/collaborators/transport/grpc/server.go b/pkg/collaborators/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..fa96dd487e740a10eadc1efbf8c10509c06940cb
--- /dev/null
+++ b/pkg/collaborators/transport/grpc/server.go
@@ -0,0 +1,21 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/collaborators"
+	"git.perx.ru/perxis/perxis-go/pkg/collaborators/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	pb "git.perx.ru/perxis/perxis-go/proto/collaborators"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc collaborators.Collaborators, opts ...grpckit.ServerOption) pb.CollaboratorsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		GetEndpoint:               grpcerr.ServerMiddleware(eps.GetEndpoint),
+		ListSpacesEndpoint:        grpcerr.ServerMiddleware(eps.ListSpacesEndpoint),
+		ListCollaboratorsEndpoint: grpcerr.ServerMiddleware(eps.ListCollaboratorsEndpoint),
+		RemoveEndpoint:            grpcerr.ServerMiddleware(eps.RemoveEndpoint),
+		SetEndpoint:               grpcerr.ServerMiddleware(eps.SetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/collections/collection.go b/pkg/collections/collection.go
index 85ab01999816f8ab986c69495d737ce3f1e8160e..b5d2418aac40332c99b08b31a5df128007e1b7a6 100644
--- a/pkg/collections/collection.go
+++ b/pkg/collections/collection.go
@@ -3,6 +3,7 @@ package collections
 import (
 	"time"
 
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 	"git.perx.ru/perxis/perxis-go/pkg/permission"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 )
@@ -51,16 +52,28 @@ func (a Access) Can(action permission.Action) bool {
 }
 
 type Collection struct {
-	ID      string         `json:"id" bson:"id"`
-	SpaceID string         `json:"spaceId" bson:"-"`
-	EnvID   string         `json:"envId" bson:"-"`
-	Name    string         `json:"name" bson:"name"`
-	Single  *bool          `json:"single" bson:"single,omitempty"` // В коллекции может быть только один документ
-	System  *bool          `json:"system" bson:"system,omitempty"` // Системная коллекция
-	NoData  *bool          `json:"no_data" bson:"no_data"`         // Коллекция не содержит элементы. Схема используется для включения в другие схемы
-	Hidden  bool           `json:"hidden" bson:"hidden"`           // Коллекция скрыта в административном интерфейсе
-	Schema  *schema.Schema `json:"schema" bson:"schema"`
-	Access  *Access        `json:"access" bson:"-"` // Ограничения на доступ к элементам коллекции. Отсутствие объекта означает неограниченный доступ
+	ID      string `json:"id" bson:"id"`
+	SpaceID string `json:"spaceId" bson:"-"`
+	EnvID   string `json:"envId" bson:"-"`
+	Name    string `json:"name" bson:"name"`
+	Single  *bool  `json:"single" bson:"single,omitempty"` // В коллекции может быть только один документ
+	System  *bool  `json:"system" bson:"system,omitempty"` // Системная коллекция
+	NoData  *bool  `json:"no_data" bson:"no_data"`         // Коллекция не содержит элементы. Схема используется для включения в другие схемы
+	Hidden  bool   `json:"hidden" bson:"hidden"`           // Коллекция скрыта в административном интерфейсе
+
+	NoArchive bool `json:"no_archive" bson:"no_archive,omitempty"` // Коллекция без архива
+
+	NoRevisions  bool          `json:"no_revisions" bson:"no_revisions,omitempty"`   // Не хранить историю изменений
+	MaxRevisions uint32        `json:"max_revisions" bson:"max_revisions,omitempty"` // Максимальное количество хранимых ревизий
+	RevisionTTL  time.Duration `json:"revision_ttl" bson:"revision_ttl,omitempty"`   // Время жизни ревизии
+
+	// Все записи коллекции считаются опубликованными, функции публикации и снятия с публикации недоступны.
+	// При включении параметра коллекции "без публикации" все записи, независимо от статуса, будут считаться опубликованными.
+	// При отключении параметра "без публикации" статусы публикации будут восстановлены.
+	NoPublish bool `json:"no_publish" bson:"no_publish,omitempty"`
+
+	Schema *schema.Schema `json:"schema" bson:"schema"`
+	Access *Access        `json:"access" bson:"-"` // Ограничения на доступ к элементам коллекции. Отсутствие объекта означает неограниченный доступ
 
 	// StateInfo отображает состояние коллекции:
 	// - State: идентификатор состояния коллекции (new/preparing/ready/error/changed)
@@ -79,6 +92,33 @@ type Collection struct {
 	Config *Config `json:"-" bson:"-"`
 }
 
+// GetID возвращает идентификатор коллекции
+func (c Collection) GetID() string {
+	return c.ID
+}
+
+// Equal сравнивает две коллекции, за исключением Schema, Access, StateInfo и Config
+func (c Collection) Equal(other *Collection) bool {
+	if c.ID != other.ID ||
+		c.SpaceID != other.SpaceID ||
+		c.EnvID != other.EnvID ||
+		c.Name != other.Name ||
+		c.IsNoData() != other.IsNoData() ||
+		c.IsSingle() != other.IsSingle() ||
+		c.IsSystem() != other.IsSystem() ||
+		c.Hidden != other.Hidden ||
+		c.NoPublish != other.NoPublish ||
+		c.NoArchive != other.NoArchive ||
+		c.NoRevisions != other.NoRevisions ||
+		c.MaxRevisions != other.MaxRevisions ||
+		c.RevisionTTL != other.RevisionTTL ||
+		!c.View.Equal(other.View) ||
+		!data.ElementsMatch(c.Tags, other.Tags) {
+		return false
+	}
+	return true
+}
+
 type View struct {
 	SpaceID      string `json:"space_id" bson:"space_id"`             // SpaceID оригинальной коллекции
 	EnvID        string `json:"environment_id" bson:"environment_id"` // EnvID оригинальной коллекции
@@ -133,14 +173,18 @@ const (
 )
 
 func (c Collection) Clone() *Collection {
-
 	clone := &Collection{
-		ID:      c.ID,
-		SpaceID: c.SpaceID,
-		EnvID:   c.EnvID,
-		Name:    c.Name,
-		NoData:  c.NoData,
-		Hidden:  c.Hidden,
+		ID:           c.ID,
+		SpaceID:      c.SpaceID,
+		EnvID:        c.EnvID,
+		Name:         c.Name,
+		NoData:       c.NoData,
+		Hidden:       c.Hidden,
+		NoPublish:    c.NoPublish,
+		NoArchive:    c.NoArchive,
+		NoRevisions:  c.NoRevisions,
+		MaxRevisions: c.MaxRevisions,
+		RevisionTTL:  c.RevisionTTL,
 	}
 
 	if c.Single != nil {
diff --git a/pkg/collections/marshal.go b/pkg/collections/marshal.go
new file mode 100644
index 0000000000000000000000000000000000000000..21c7bed0c5a8edb83780a19d31045d8b8c07f9ef
--- /dev/null
+++ b/pkg/collections/marshal.go
@@ -0,0 +1,92 @@
+package collections
+
+import (
+	"reflect"
+	"strconv"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/optional"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	jsoniter "github.com/json-iterator/go"
+)
+
+// UnmarshalJSON implements json.Unmarshaler interface
+func (c *Collection) UnmarshalJSON(b []byte) error {
+	type collection Collection
+	var cc, zero collection
+
+	// Пытаемся распарсить как коллекцию
+	if err := jsoniter.Unmarshal(b, &cc); err != nil {
+		return err
+	}
+
+	// Если это не пустая коллекция, то просто присваиваем
+	if !reflect.DeepEqual(cc, zero) {
+		*c = Collection(cc)
+		return nil
+	}
+
+	// Пытаемся распарсить как схему
+	var s schema.Schema
+	if err := jsoniter.Unmarshal(b, &s); err != nil {
+		return err
+	}
+	*c = *FromSchema(&s)
+	return nil
+}
+
+// FromSchema создает новую коллекцию из схемы
+func FromSchema(sch *schema.Schema) *Collection {
+	if sch == nil {
+		return nil
+	}
+
+	coll := &Collection{Schema: sch}
+
+	coll.ID = sch.Metadata["collection_id"]
+	coll.Name = sch.Metadata["collection_name"]
+
+	if single, ok := sch.Metadata["collection_single"]; ok && single == "true" {
+		coll.Single = optional.True
+	}
+	if system, ok := sch.Metadata["collection_system"]; ok && system == "true" {
+		coll.System = optional.True
+	}
+	if nodata, ok := sch.Metadata["collection_nodata"]; ok && nodata == "true" {
+		coll.NoData = optional.True
+	}
+	if hidden, ok := sch.Metadata["collection_hidden"]; ok && hidden == "true" {
+		coll.Hidden = true
+	}
+	if disablePublishing, ok := sch.Metadata["collection_no_publish"]; ok && disablePublishing == "true" {
+		coll.NoPublish = true
+	}
+	if noArchive, ok := sch.Metadata["collection_no_archive"]; ok && noArchive == "true" {
+		coll.NoArchive = true
+	}
+
+	if noRevisions, ok := sch.Metadata["collection_no_revisions"]; ok && noRevisions == "true" {
+		coll.NoRevisions = true
+	}
+	if mr, ok := sch.Metadata["collection_max_revisions"]; ok {
+		if maxRevisions, err := strconv.ParseUint(mr, 10, 32); err == nil {
+			coll.MaxRevisions = uint32(maxRevisions)
+		}
+	}
+	if ttl, ok := sch.Metadata["collection_revisions_ttl"]; ok {
+		if revisionTTL, err := time.ParseDuration(ttl); err == nil {
+			coll.RevisionTTL = revisionTTL
+		}
+	}
+
+	if _, ok := sch.Metadata["collection_view_id"]; ok {
+		coll.View = &View{
+			SpaceID:      sch.Metadata["collection_view_space"],
+			EnvID:        sch.Metadata["collection_view_env"],
+			CollectionID: sch.Metadata["collection_view_id"],
+			Filter:       sch.Metadata["collection_view_filter"],
+		}
+	}
+
+	return coll
+}
diff --git a/pkg/collections/marshal_test.go b/pkg/collections/marshal_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2cc1bf52499422160d189a1e454144de39da4c95
--- /dev/null
+++ b/pkg/collections/marshal_test.go
@@ -0,0 +1,130 @@
+package collections
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/optional"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	jsoniter "github.com/json-iterator/go"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestCollection_UnmarshalJSON(t *testing.T) {
+	sch := schema.New("a", field.String()).WithMetadata(
+		"collection_id", "collID",
+		"collection_name", "collName")
+	sch.ClearState()
+
+	tests := []struct {
+		name    string
+		in      any
+		expect  *Collection
+		wantErr bool
+	}{
+		{name: "from schema",
+			in:      sch,
+			expect:  &Collection{Schema: sch, ID: "collID", Name: "collName"},
+			wantErr: false},
+		{name: "from collection",
+			in:      &Collection{Schema: sch, ID: "id", Name: "name"},
+			expect:  &Collection{Schema: sch, ID: "id", Name: "name"},
+			wantErr: false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var c Collection
+			b, _ := jsoniter.Marshal(tt.in)
+			if err := jsoniter.Unmarshal(b, &c); (err != nil) != tt.wantErr {
+				t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
+			}
+
+			assert.True(t, c.Equal(tt.expect))
+			assert.True(t, c.Schema.Equal(tt.expect.Schema))
+		})
+	}
+}
+
+func TestFromSchemaMetadata(t *testing.T) {
+	testCases := []struct {
+		name   string
+		schema *schema.Schema
+		want   *Collection
+	}{
+		{
+			name:   "Nil",
+			schema: nil,
+			want:   nil,
+		},
+		{
+			name:   "Empty",
+			schema: &schema.Schema{},
+			want:   &Collection{Schema: &schema.Schema{}},
+		},
+		{
+			name:   "Without metadata",
+			schema: schema.New("a", field.String()),
+			want:   &Collection{Schema: schema.New("a", field.String())},
+		},
+		{
+			name: "With metadata",
+			schema: schema.New("a", field.String()).WithMetadata(
+				"collection_id", "collID",
+				"collection_name", "collName",
+				"collection_single", "true",
+				"collection_system", "true",
+				"collection_nodata", "true",
+				"collection_hidden", "true",
+				"collection_no_publish", "true",
+				"collection_no_archive", "true",
+				"collection_view_space", "viewSpaceID",
+				"collection_view_env", "viewEnvID",
+				"collection_view_id", "viewCollID",
+				"collection_view_filter", "viewFilter",
+			),
+			want: &Collection{
+				ID:        "collID",
+				Name:      "collName",
+				Single:    optional.True,
+				System:    optional.True,
+				NoData:    optional.True,
+				Hidden:    true,
+				NoPublish: true,
+				NoArchive: true,
+				Schema:    schema.New("a", field.String()).WithMetadata("collection_id", "collID", "collection_name", "collName", "collection_single", "true", "collection_system", "true", "collection_nodata", "true", "collection_hidden", "true", "collection_no_publish", "true", "collection_no_archive", "true", "collection_view_space", "viewSpaceID", "collection_view_env", "viewEnvID", "collection_view_id", "viewCollID", "collection_view_filter", "viewFilter"),
+				View: &View{
+					SpaceID:      "viewSpaceID",
+					EnvID:        "viewEnvID",
+					CollectionID: "viewCollID",
+					Filter:       "viewFilter",
+				},
+			},
+		},
+		{
+			name: "With metadata revisions settings",
+			schema: schema.New("a", field.String()).WithMetadata(
+				"collection_id", "collID",
+				"collection_name", "collName",
+				"collection_no_revisions", "true",
+				"collection_max_revisions", "10",
+				"collection_revisions_ttl", "1h",
+			),
+			want: &Collection{
+				ID:           "collID",
+				Name:         "collName",
+				NoRevisions:  true,
+				MaxRevisions: 10,
+				RevisionTTL:  3600000000000,
+				Schema:       schema.New("a", field.String()).WithMetadata("collection_id", "collID", "collection_name", "collName", "collection_no_revisions", "true", "collection_max_revisions", "10", "collection_revisions_ttl", "1h"),
+			},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			result := FromSchema(tt.schema)
+			require.Equal(t, tt.want, result)
+		})
+	}
+}
diff --git a/pkg/collections/middleware/caching_middleware.go b/pkg/collections/middleware/caching_middleware.go
index de34d42af02f7259c66aea5799210c50c0e1961e..cb95d0d9a8772f91700f5f630fa9bb2631e51937 100644
--- a/pkg/collections/middleware/caching_middleware.go
+++ b/pkg/collections/middleware/caching_middleware.go
@@ -44,7 +44,7 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId string, envId string
 	opts := service.MergeGetOptions(options...)
 	value, e := m.cache.Get(makeKey(spaceId, envId, collectionId, opts.DisableSchemaIncludes))
 	if e == nil {
-		return value.(*service.Collection), err
+		return value.(*service.Collection).Clone(), nil
 	}
 	coll, err = m.next.Get(ctx, spaceId, envId, collectionId, options...)
 	if err == nil {
@@ -52,13 +52,13 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId string, envId string
 		if err != nil {
 			return nil, err
 		}
-		m.cache.Set(makeKey(coll.SpaceID, env.ID, coll.ID, opts.DisableSchemaIncludes), coll)
+		_ = m.cache.Set(makeKey(coll.SpaceID, env.ID, coll.ID, opts.DisableSchemaIncludes), coll)
 		for _, al := range env.Aliases {
-			m.cache.Set(makeKey(coll.SpaceID, al, coll.ID, opts.DisableSchemaIncludes), coll)
+			_ = m.cache.Set(makeKey(coll.SpaceID, al, coll.ID, opts.DisableSchemaIncludes), coll)
 		}
-
+		return coll.Clone(), nil
 	}
-	return coll, err
+	return nil, err
 }
 
 func (m cachingMiddleware) List(ctx context.Context, spaceId, envId string, filter *service.Filter) (collections []*service.Collection, err error) {
@@ -73,11 +73,11 @@ func (m cachingMiddleware) Update(ctx context.Context, coll *service.Collection)
 		if err != nil {
 			return err
 		}
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, coll.ID, true))
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, coll.ID, false))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, coll.ID, true))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, coll.ID, false))
 		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(env.SpaceID, al, coll.ID, true))
-			m.cache.Remove(makeKey(env.SpaceID, al, coll.ID, false))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, coll.ID, true))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, coll.ID, false))
 		}
 	}
 	return err
@@ -90,11 +90,11 @@ func (m cachingMiddleware) SetSchema(ctx context.Context, spaceId, envId, collec
 		if err != nil {
 			return err
 		}
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
 		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
-			m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
 		}
 	}
 	return err
@@ -107,11 +107,11 @@ func (m cachingMiddleware) SetState(ctx context.Context, spaceId, envId, collect
 		if err != nil {
 			return err
 		}
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
 		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
-			m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
 		}
 	}
 	return err
@@ -125,11 +125,11 @@ func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, envId str
 		if err != nil {
 			return err
 		}
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
-		m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
 		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
-			m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
+			_ = m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
 		}
 	}
 	return err
diff --git a/pkg/collections/middleware/caching_middleware_test.go b/pkg/collections/middleware/caching_middleware_test.go
index ac284ce5464af7469fee9ad4a7babf377b9c335a..24646008ba32736ca45f7bcdc7e0a4845c43877d 100644
--- a/pkg/collections/middleware/caching_middleware_test.go
+++ b/pkg/collections/middleware/caching_middleware_test.go
@@ -46,11 +46,13 @@ func TestCollections_Cache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, envID, colID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе по ID окружения.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе по ID окружения.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.Get(ctx, spaceID, envAlias, colID)
 		require.NoError(t, err)
-		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+		assert.Equal(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+		assert.NotSame(t, v3, v2)
 
 		env.AssertExpectations(t)
 		col.AssertExpectations(t)
@@ -70,11 +72,13 @@ func TestCollections_Cache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, envAlias, colID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе по Alias окружения.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе по Alias окружения.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.Get(ctx, spaceID, envID, colID)
 		require.NoError(t, err)
-		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+		assert.Equal(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+		assert.NotSame(t, v3, v2)
 
 		env.AssertExpectations(t)
 		col.AssertExpectations(t)
@@ -115,12 +119,12 @@ func TestCollections_Cache(t *testing.T) {
 	//	vl2, err := svc.List(ctx, spaceID, envID, nil)
 	//	require.NoError(t, err)
 	//	assert.Len(t, vl2, 1)
-	//	assert.Same(t, vl1[0], vl2[0], "При повторном запросе по ID окружения, ожидается получение списка объектов из кеша.")
+	//	assert.Equal(t, vl1[0], vl2[0], "При повторном запросе по ID окружения, ожидается получение списка объектов из кеша.")
 	//
 	//	vl3, err := svc.List(ctx, spaceID, envAlias, nil)
 	//	require.NoError(t, err)
 	//	assert.Len(t, vl3, 1)
-	//	assert.Same(t, vl3[0], vl2[0], "При повторном запросе по Alias окружения, ожидается получение списка объектов из кеша.")
+	//	assert.Equal(t, vl3[0], vl2[0], "При повторном запросе по Alias окружения, ожидается получение списка объектов из кеша.")
 	//
 	//	env.AssertExpectations(t)
 	//	col.AssertExpectations(t)
@@ -161,11 +165,13 @@ func TestCollections_Cache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
@@ -181,15 +187,16 @@ func TestCollections_Cache(t *testing.T) {
 
 			v4, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v4, "Ожидает что элемент после обновления был удален из кэша и будет запрошен заново из сервиса.")
+			assert.NotEqual(t, v3, v4, "Ожидает что элемент после обновления был удален из кэша и будет запрошен заново из сервиса.")
 
 			v5, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.Same(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v4, v5)
 
 			vl2, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
-			assert.NotSame(t, vl1[0], vl2[0], "Ожидает что после обновления элементы будут запрошены заново из сервиса.")
+			assert.NotEqual(t, vl1[0], vl2[0], "Ожидает что после обновления элементы будут запрошены заново из сервиса.")
 
 			env.AssertExpectations(t)
 			col.AssertExpectations(t)
@@ -212,11 +219,13 @@ func TestCollections_Cache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID, envAlias, nil)
 			require.NoError(t, err)
@@ -234,15 +243,16 @@ func TestCollections_Cache(t *testing.T) {
 
 			v4, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v4, "Ожидает что элемент после обновления был удален из кэша и будет запрошен заново из сервиса.")
+			assert.NotEqual(t, v3, v4, "Ожидает что элемент после обновления был удален из кэша и будет запрошен заново из сервиса.")
 
 			v5, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.Same(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v4, v5)
 
 			vl4, err := svc.List(ctx, spaceID, envAlias, nil)
 			require.NoError(t, err)
-			assert.NotSame(t, vl1[0], vl4[0], "Ожидает что после обновления элементы будут запрошены заново из сервиса.")
+			assert.NotEqual(t, vl1[0], vl4[0], "Ожидает что после обновления элементы будут запрошены заново из сервиса.")
 
 			env.AssertExpectations(t)
 			col.AssertExpectations(t)
@@ -264,11 +274,13 @@ func TestCollections_Cache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
@@ -276,7 +288,7 @@ func TestCollections_Cache(t *testing.T) {
 			vl2, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
 			assert.Len(t, vl2, 1)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
 
 			vl3, err := svc.List(ctx, spaceID, envAlias, nil)
 			require.NoError(t, err)
@@ -295,15 +307,16 @@ func TestCollections_Cache(t *testing.T) {
 
 			v4, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v4, "Ожидает что элемент после обновления схемы был удален из кэша и будет запрошен заново из сервиса.")
+			assert.NotEqual(t, v3, v4, "Ожидает что элемент после обновления схемы был удален из кэша и будет запрошен заново из сервиса.")
 
 			v5, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.Same(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v4, v5)
 
 			vl4, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
-			assert.NotSame(t, vl4[0], vl3[0], "Ожидает что после обновления схемы элементы будут запрошены заново из сервиса.")
+			assert.NotEqual(t, vl4[0], vl3[0], "Ожидает что после обновления схемы элементы будут запрошены заново из сервиса.")
 
 			vl5, err := svc.List(ctx, spaceID, envAlias, nil)
 			require.NoError(t, err)
@@ -329,11 +342,13 @@ func TestCollections_Cache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envAlias, colID)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
@@ -341,7 +356,7 @@ func TestCollections_Cache(t *testing.T) {
 			vl2, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
 			assert.Len(t, vl2, 1)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
 
 			vl3, err := svc.List(ctx, spaceID, envAlias, nil)
 			require.NoError(t, err)
@@ -391,7 +406,7 @@ func TestCollections_Cache(t *testing.T) {
 			vl2, err := svc.List(ctx, spaceID, envID, nil)
 			require.NoError(t, err)
 			assert.Len(t, vl2, 1)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
 
 			vl3, err := svc.List(ctx, spaceID, envAlias, nil)
 			require.NoError(t, err)
@@ -440,7 +455,8 @@ func TestCollections_Cache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 
@@ -449,6 +465,8 @@ func TestCollections_Cache(t *testing.T) {
 			v3, err := svc.Get(ctx, spaceID, envID, colID)
 			require.NoError(t, err)
 			assert.NotSame(t, v3, v2, "Ожидает что элемент был удален из кэша и будет запрошен заново из сервиса.")
+			assert.Equal(t, v3, v2)
+			assert.NotSame(t, v3, v2)
 
 			env.AssertExpectations(t)
 			col.AssertExpectations(t)
diff --git a/pkg/collections/middleware/logging_middleware.go b/pkg/collections/middleware/logging_middleware.go
index 3c80eda411e9905791e59042a31f2af8f300cefd..92e2811bd2f30cd45eb0b1aa66da1d52cfbb2596 100644
--- a/pkg/collections/middleware/logging_middleware.go
+++ b/pkg/collections/middleware/logging_middleware.go
@@ -30,7 +30,7 @@ func (m *loggingMiddleware) Create(ctx context.Context, collection *collections.
 		spaceID = collection.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(collections.EventCreate),
 	)
 
@@ -46,7 +46,7 @@ func (m *loggingMiddleware) Create(ctx context.Context, collection *collections.
 
 func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId string) (err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Event(collections.EventDelete),
 		logzap.Object(id.NewCollectionId(spaceId, envId, collectionId)),
 	)
@@ -63,7 +63,7 @@ func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId st
 
 func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	collection, err = m.next.Get(ctx, spaceId, envId, collectionId, options...)
@@ -77,7 +77,7 @@ func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId strin
 
 func (m *loggingMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	collections, err = m.next.List(ctx, spaceId, envId, filter)
@@ -91,7 +91,7 @@ func (m *loggingMiddleware) List(ctx context.Context, spaceId string, envId stri
 
 func (m *loggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Event(collections.EventSetSchema),
 		logzap.Object(id.NewCollectionId(spaceId, envId, collectionId)),
 	)
@@ -108,7 +108,7 @@ func (m *loggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId
 
 func (m *loggingMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	err = m.next.SetState(ctx, spaceId, envId, collectionId, state)
@@ -127,7 +127,7 @@ func (m *loggingMiddleware) Update(ctx context.Context, coll *collections.Collec
 		spaceID = coll.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(collections.EventUpdate),
 		logzap.Object(coll),
 	)
diff --git a/pkg/collections/observer.go b/pkg/collections/observer.go
index 8d4a6b7d14ec687c97eec6b0406fcc32d515aca2..8df5c084312d9099e200997ed27a9cde921cbb72 100644
--- a/pkg/collections/observer.go
+++ b/pkg/collections/observer.go
@@ -14,8 +14,14 @@ type CollectionUpdatedObserver interface {
 	OnCollectionUpdated(ctx context.Context, before, after *Collection) (delayedTaskID string, err error)
 }
 
+// CollectionPreSetSchemaObserver интерфейс наблюдателя вызываемый перед изменением схемы коллекции.
+// Инициировать оповещение наблюдателя может вызов методов `Collection.SetSchema`
+type CollectionPreSetSchemaObserver interface {
+	OnCollectionPreSetSchema(ctx context.Context, before, coll *Collection) (delayedTaskID string, err error)
+}
+
 // CollectionSetSchemaObserver интерфейс наблюдателя вызываемый при изменении схемы коллекции.
-// Инициировать оповещение наблюдателя может вызов методов `Collection.Schema`
+// Инициировать оповещение наблюдателя может вызов методов `Collection.SetSchema`
 type CollectionSetSchemaObserver interface {
 	OnCollectionSetSchema(ctx context.Context, before, coll *Collection) (delayedTaskID string, err error)
 }
diff --git a/pkg/collections/transport/client.microgen.go b/pkg/collections/transport/client.go
similarity index 71%
rename from pkg/collections/transport/client.microgen.go
rename to pkg/collections/transport/client.go
index 7fd5b53f360a927e48c00edcd3f4dc7606dd47fe..2a430e6c8893236c73c7979654c282cb6094b807 100644
--- a/pkg/collections/transport/client.microgen.go
+++ b/pkg/collections/transport/client.go
@@ -4,21 +4,15 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
 	schema "git.perx.ru/perxis/perxis-go/pkg/schema"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *collections.Collection) (res0 *collections.Collection, res1 error) {
 	request := CreateRequest{Collection: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
@@ -33,9 +27,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string, arg3
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Collection, res1
@@ -49,9 +40,6 @@ func (set EndpointsSet) List(arg0 context.Context, arg1 string, arg2 string, arg
 	}
 	response, res1 := set.ListEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListResponse).Collections, res1
@@ -61,9 +49,6 @@ func (set EndpointsSet) Update(arg0 context.Context, arg1 *collections.Collectio
 	request := UpdateRequest{Coll: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -78,9 +63,6 @@ func (set EndpointsSet) SetSchema(arg0 context.Context, arg1 string, arg2 string
 	}
 	_, res0 = set.SetSchemaEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -98,9 +80,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string, a
 	}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/collections/transport/grpc/client.go b/pkg/collections/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..8051b9b97cecf06a603d60925145bb55a7162a9d
--- /dev/null
+++ b/pkg/collections/transport/grpc/client.go
@@ -0,0 +1,22 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/collections/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint:    grpcerr.ClientMiddleware(c.CreateEndpoint),
+		DeleteEndpoint:    grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		GetEndpoint:       grpcerr.ClientMiddleware(c.GetEndpoint),
+		ListEndpoint:      grpcerr.ClientMiddleware(c.ListEndpoint),
+		SetSchemaEndpoint: grpcerr.ClientMiddleware(c.SetSchemaEndpoint),
+		UpdateEndpoint:    grpcerr.ClientMiddleware(c.UpdateEndpoint),
+	}
+}
diff --git a/pkg/collections/transport/grpc/protobuf_type_converters.microgen.go b/pkg/collections/transport/grpc/protobuf_type_converters.microgen.go
index 0072506b7658a5591b09f914780706fcef88d034..b946a8969f863172f851a3eed83a1d1478ace45f 100644
--- a/pkg/collections/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/collections/transport/grpc/protobuf_type_converters.microgen.go
@@ -13,6 +13,7 @@ import (
 	pb "git.perx.ru/perxis/perxis-go/proto/collections"
 	commonpb "git.perx.ru/perxis/perxis-go/proto/common"
 	jsoniter "github.com/json-iterator/go"
+	"google.golang.org/protobuf/types/known/durationpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
@@ -61,16 +62,21 @@ func PtrCollectionToProto(coll *service.Collection) (*pb.Collection, error) {
 		}
 	}
 	protoCollection := &pb.Collection{
-		Id:      coll.ID,
-		SpaceId: coll.SpaceID,
-		EnvId:   coll.EnvID,
-		Name:    coll.Name,
-		Single:  coll.Single,
-		System:  coll.System,
-		NoData:  coll.NoData,
-		Access:  access,
-		Hidden:  coll.Hidden,
-		Tags:    coll.Tags,
+		Id:           coll.ID,
+		SpaceId:      coll.SpaceID,
+		EnvId:        coll.EnvID,
+		Name:         coll.Name,
+		Single:       coll.Single,
+		System:       coll.System,
+		NoData:       coll.NoData,
+		Access:       access,
+		Hidden:       coll.Hidden,
+		Tags:         coll.Tags,
+		NoPublish:    coll.NoPublish,
+		NoArchive:    coll.NoArchive,
+		NoRevisions:  coll.NoRevisions,
+		MaxRevisions: coll.MaxRevisions,
+		RevisionTtl:  durationpb.New(coll.RevisionTTL),
 	}
 
 	if coll.StateInfo != nil {
@@ -125,16 +131,21 @@ func ProtoToPtrCollection(protoCollection *pb.Collection) (*service.Collection,
 		}
 	}
 	collection := &service.Collection{
-		ID:      protoCollection.Id,
-		SpaceID: protoCollection.SpaceId,
-		EnvID:   protoCollection.EnvId,
-		Name:    protoCollection.Name,
-		Single:  protoCollection.Single,
-		System:  protoCollection.System,
-		NoData:  protoCollection.NoData,
-		Access:  access,
-		Hidden:  protoCollection.Hidden,
-		Tags:    protoCollection.Tags,
+		ID:           protoCollection.Id,
+		SpaceID:      protoCollection.SpaceId,
+		EnvID:        protoCollection.EnvId,
+		Name:         protoCollection.Name,
+		Single:       protoCollection.Single,
+		System:       protoCollection.System,
+		NoData:       protoCollection.NoData,
+		Access:       access,
+		Hidden:       protoCollection.Hidden,
+		Tags:         protoCollection.Tags,
+		NoPublish:    protoCollection.NoPublish,
+		NoArchive:    protoCollection.NoArchive,
+		NoRevisions:  protoCollection.NoRevisions,
+		MaxRevisions: protoCollection.MaxRevisions,
+		RevisionTTL:  protoCollection.RevisionTtl.AsDuration(),
 	}
 
 	if protoCollection.StateInfo != nil {
diff --git a/pkg/collections/transport/grpc/server.go b/pkg/collections/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..6482f1c416d7b0792b9ca2244a144de0bea627c1
--- /dev/null
+++ b/pkg/collections/transport/grpc/server.go
@@ -0,0 +1,22 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"git.perx.ru/perxis/perxis-go/pkg/collections/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	pb "git.perx.ru/perxis/perxis-go/proto/collections"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc collections.Collections, opts ...grpckit.ServerOption) pb.CollectionsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint:    grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		DeleteEndpoint:    grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		GetEndpoint:       grpcerr.ServerMiddleware(eps.GetEndpoint),
+		ListEndpoint:      grpcerr.ServerMiddleware(eps.ListEndpoint),
+		SetSchemaEndpoint: grpcerr.ServerMiddleware(eps.SetSchemaEndpoint),
+		UpdateEndpoint:    grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/content/client.go b/pkg/content/client.go
index bbdd62bb9474afd54ce63606055c244311478adc..adc5f784dff8fccd5b505867bf65efb373a9149d 100644
--- a/pkg/content/client.go
+++ b/pkg/content/client.go
@@ -47,14 +47,14 @@ func NewClient(conn *grpc.ClientConn, opts ...Option) *Content {
 	}
 
 	client.Spaces = spacesTransportGrpc.NewClient(conn, config.ClientOptions...)
-	client.Environments = environmentsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
-	client.Collections = collectionsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
+	client.Environments = environmentsTransportGrpc.NewClient(conn, config.ClientOptions...)
+	client.Collections = collectionsTransportGrpc.NewClient(conn, config.ClientOptions...)
 	client.Items = itemsTransportGrpc.NewClient(conn, config.ClientOptions...)
-	client.Invitations = invitationsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
-	client.Collaborators = collaboratorsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
-	client.Clients = clientsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
-	client.Locales = localsTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
-	client.Roles = rolesTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
+	client.Invitations = invitationsTransportGrpc.NewClient(conn, config.ClientOptions...)
+	client.Collaborators = collaboratorsTransportGrpc.NewClient(conn, config.ClientOptions...)
+	client.Clients = clientsTransportGrpc.NewClient(conn, config.ClientOptions...)
+	client.Locales = localsTransportGrpc.NewClient(conn, config.ClientOptions...)
+	client.Roles = rolesTransportGrpc.NewClient(conn, config.ClientOptions...)
 	client.References = referencesTransportGrpc.NewGRPCClient(conn, "", config.ClientOptions...)
 
 	if !config.NoDecode {
diff --git a/pkg/account/versions/transport/client.microgen.go b/pkg/content/versions/transport/client.go
similarity index 62%
rename from pkg/account/versions/transport/client.microgen.go
rename to pkg/content/versions/transport/client.go
index ec9a69655fab437a4146b6cc61973197f415ff2c..0713d94abcaca463a18d5e129314c0b8769a3ba4 100644
--- a/pkg/account/versions/transport/client.microgen.go
+++ b/pkg/content/versions/transport/client.go
@@ -4,20 +4,14 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	version "git.perx.ru/perxis/perxis-go/pkg/version"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Get(arg0 context.Context) (res0 *version.Version, res1 error) {
 	request := GetRequest{}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Version, res1
diff --git a/pkg/content/versions/transport/grpc/client.go b/pkg/content/versions/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..ffa435b90b00b707f097021f39fcea705e647ed6
--- /dev/null
+++ b/pkg/content/versions/transport/grpc/client.go
@@ -0,0 +1,17 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/content/versions/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		GetEndpoint: grpcerr.ClientMiddleware(c.GetEndpoint),
+	}
+}
diff --git a/pkg/content/versions/transport/grpc/server.go b/pkg/content/versions/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..26b30d26150d8e8cb7cc4544f3d59f351ec4b56a
--- /dev/null
+++ b/pkg/content/versions/transport/grpc/server.go
@@ -0,0 +1,17 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/content/versions/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	versions "git.perx.ru/perxis/perxis-go/pkg/version"
+	pb "git.perx.ru/perxis/perxis-go/proto/versions/content"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc versions.Versions, opts ...grpckit.ServerOption) pb.VersionsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		GetEndpoint: grpcerr.ServerMiddleware(eps.GetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/data/data.go b/pkg/data/data.go
index 0540055ad4e213f666b0cf72019b9b4b9c39fbe8..14528479855b9993e6cdce218ac087b41f334c58 100644
--- a/pkg/data/data.go
+++ b/pkg/data/data.go
@@ -120,7 +120,7 @@ func DeleteMany(paths []string, value any, delim ...string) {
 		return
 	}
 	for _, path := range paths {
-		Delete(path, value, delim...)
+		_ = Delete(path, value, delim...)
 	}
 }
 
@@ -245,7 +245,6 @@ func get(path []string, data any) (any, bool) {
 // The path is the sting with delim, for eg:, parent.child.key
 func Keep(paths []string, data any, delim ...string) {
 	if len(paths) == 0 {
-		data = nil
 		return
 	}
 	switch val := data.(type) {
diff --git a/pkg/data/data_test.go b/pkg/data/data_test.go
index 785eefbb868a68c9d8c6b2f75b8f861ab2041e11..463a4cbc01ad7cfd5c249a026a89f733f3c54e60 100644
--- a/pkg/data/data_test.go
+++ b/pkg/data/data_test.go
@@ -63,22 +63,23 @@ func TestDelete(t *testing.T) {
 			}, "z": "2"},
 		},
 		// Решили что автоматически удалять пустые объекты/слайсы не нужно
-		//{
+		// {
 		//	"empty object",
 		//	map[string]interface{}{"a": map[string]interface{}{"a": map[string]interface{}{}}},
 		//	[]string{"a", "a"},
 		//	map[string]interface{}{},
-		//}, {
+		// }, {
 		//	"empty array",
 		//	map[string]interface{}{"a": map[string]interface{}{"a": []interface{}{}}},
 		//	[]string{"a", "a"},
 		//	map[string]interface{}{},
-		//},
+		// },
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			Delete(tt.field, tt.in)
+			err := Delete(tt.field, tt.in)
+			assert.NoError(t, err)
 			assert.Equal(t, tt.out, tt.in)
 		})
 	}
diff --git a/pkg/data/list.go b/pkg/data/list.go
index e15a20cadcc2a22bc9c4797b8fa16413a511b5ab..4481a18861c3524c8636b4f9fbfbdf14efb6f711 100644
--- a/pkg/data/list.go
+++ b/pkg/data/list.go
@@ -184,3 +184,29 @@ func mergeMaps(a, b map[string]interface{}) map[string]interface{} {
 	}
 	return out
 }
+
+// CloneSlice возвращает копию среза с клонированными элементами
+func CloneSlice[T interface{ Clone() T }](s []T) []T {
+	// Если s == nil, то возвращаем nil
+	if s == nil {
+		return nil
+	}
+
+	result := make([]T, 0, len(s))
+	for _, t := range s {
+		result = append(result, t.Clone())
+	}
+	return result
+}
+
+type Keyed[T comparable] interface {
+	Key() T
+}
+
+func SliceToMap[K comparable, V Keyed[K]](s []V) map[K]V {
+	res := make(map[K]V, len(s))
+	for _, elem := range s {
+		res[elem.Key()] = elem
+	}
+	return res
+}
diff --git a/pkg/data/list_test.go b/pkg/data/list_test.go
index 8231aae992e91f09f50bb71fb1e028e148c35993..c77c226edc8ab885ebcae9cab023d496c175a486 100644
--- a/pkg/data/list_test.go
+++ b/pkg/data/list_test.go
@@ -139,3 +139,18 @@ keyB2: val20
 		})
 	}
 }
+
+type KV struct {
+	K string
+	V string
+}
+
+func (kv *KV) Key() string {
+	return kv.K
+}
+
+func TestSliceToMap(t *testing.T) {
+	s := []*KV{{"a", "1"}, {"b", "2"}, {"c", "3"}}
+	m := SliceToMap(s)
+	assert.Equal(t, map[string]*KV{"a": {"a", "1"}, "b": {"b", "2"}, "c": {"c", "3"}}, m)
+}
diff --git a/pkg/data/translit.go b/pkg/data/translit.go
index 654ae90dd305be0ffd20bfcd58c782229a27156b..11679e37cf4da7e17f81d77c10077b7533ccef98 100644
--- a/pkg/data/translit.go
+++ b/pkg/data/translit.go
@@ -45,7 +45,7 @@ func TableEncode(s string, tlm map[rune]string) string {
 		}
 
 		if unicode.IsUpper(r) {
-			tr = strings.Title(tr)
+			tr = strings.Title(tr) //nolint:staticcheck // Для обработки основных языков достаточно правил работы функции strings.Title
 		}
 
 		out.WriteString(tr)
diff --git a/pkg/delivery/service/service.go b/pkg/delivery/service/service.go
index ed1b5091c553853ec78f9f20bc3b54b3e0239834..be71148108f3296ce5a66d6a6f1f347a81671999 100644
--- a/pkg/delivery/service/service.go
+++ b/pkg/delivery/service/service.go
@@ -34,7 +34,17 @@ type deliveryService struct {
 }
 
 func (s deliveryService) ListLocales(ctx context.Context, spaceId string) (locales []*locales.Locale, err error) {
-	return s.Locales.List(ctx, spaceId)
+	res, err := s.Locales.List(ctx, spaceId)
+	if err != nil {
+		return nil, err
+	}
+	for _, l := range res {
+		if l.NoPublish || l.Disabled {
+			continue
+		}
+		locales = append(locales, l)
+	}
+	return locales, nil
 }
 
 func (s deliveryService) GetEnvironment(ctx context.Context, spaceId, envId string) (env *environments.Environment, err error) {
diff --git a/pkg/delivery/service/service_test.go b/pkg/delivery/service/service_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..88bcc16f63b3e00c84c8e02cd2939600339400d4
--- /dev/null
+++ b/pkg/delivery/service/service_test.go
@@ -0,0 +1,57 @@
+package service
+
+import (
+	"context"
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"git.perx.ru/perxis/perxis-go/pkg/locales/mocks"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_deliveryService_ListLocales(t *testing.T) {
+	tests := []struct {
+		name        string
+		mockcall    func(locSvs *mocks.Locales)
+		wantLocales []*locales.Locale
+		wantErr     bool
+	}{
+		{
+			name: "Returned locales without NoPublish and Disabled",
+			mockcall: func(locSvs *mocks.Locales) {
+				locSvs.On("List", context.Background(), "space").Return([]*locales.Locale{
+					{ID: "en_EN", SpaceID: "space", Name: "english", Code: "en", NoPublish: false, Disabled: false},
+					{ID: "de_DE", SpaceID: "space", Name: "deutsch", Code: "de", NoPublish: false, Disabled: false},
+					{ID: "ru_RU", SpaceID: "space", Name: "russian", Code: "ru", NoPublish: false, Disabled: false},
+					{ID: "by_BY", SpaceID: "space", Name: "belarus", Code: "by", NoPublish: true, Disabled: false},
+					{ID: "es_ES", SpaceID: "space", Name: "spain", Code: "by", NoPublish: false, Disabled: true}}, nil)
+			},
+			wantLocales: []*locales.Locale{
+				{ID: "en_EN", SpaceID: "space", Name: "english", Code: "en", NoPublish: false, Disabled: false},
+				{ID: "de_DE", SpaceID: "space", Name: "deutsch", Code: "de", NoPublish: false, Disabled: false},
+				{ID: "ru_RU", SpaceID: "space", Name: "russian", Code: "ru", NoPublish: false, Disabled: false},
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			localesService := &mocks.Locales{}
+			if tt.mockcall != nil {
+				tt.mockcall(localesService)
+			}
+			s := deliveryService{
+				Locales: localesService,
+			}
+
+			gotLocales, err := s.ListLocales(context.Background(), "space")
+			if tt.wantErr {
+				assert.Error(t, err)
+				return
+			}
+			assert.NoError(t, err)
+			assert.Equal(t, tt.wantLocales, gotLocales)
+			localesService.AssertExpectations(t)
+		})
+	}
+}
diff --git a/pkg/delivery/transport/client.microgen.go b/pkg/delivery/transport/client.go
similarity index 74%
rename from pkg/delivery/transport/client.microgen.go
rename to pkg/delivery/transport/client.go
index 8f2e1216f49441cf14afe4fe2fe509ba9898bca9..39c110029c1c78df3ccf4c01ef1073612fbba2c7 100644
--- a/pkg/delivery/transport/client.microgen.go
+++ b/pkg/delivery/transport/client.go
@@ -4,23 +4,17 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
 	environments "git.perx.ru/perxis/perxis-go/pkg/environments"
 	items "git.perx.ru/perxis/perxis-go/pkg/items"
 	locales "git.perx.ru/perxis/perxis-go/pkg/locales"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) ListLocales(arg0 context.Context, arg1 string) (res0 []*locales.Locale, res1 error) {
 	request := ListLocalesRequest{SpaceId: arg1}
 	response, res1 := set.ListLocalesEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListLocalesResponse).Locales, res1
@@ -33,9 +27,6 @@ func (set EndpointsSet) GetEnvironment(arg0 context.Context, arg1 string, arg2 s
 	}
 	response, res1 := set.GetEnvironmentEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetEnvironmentResponse).Env, res1
@@ -45,9 +36,6 @@ func (set EndpointsSet) ListEnvironments(arg0 context.Context, arg1 string) (res
 	request := ListEnvironmentsRequest{SpaceId: arg1}
 	response, res1 := set.ListEnvironmentsEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListEnvironmentsResponse).Envs, res1
@@ -61,9 +49,6 @@ func (set EndpointsSet) GetCollection(arg0 context.Context, arg1 string, arg2 st
 	}
 	response, res1 := set.GetCollectionEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetCollectionResponse).Collection, res1
@@ -76,9 +61,6 @@ func (set EndpointsSet) ListCollections(arg0 context.Context, arg1 string, arg2
 	}
 	response, res1 := set.ListCollectionsEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListCollectionsResponse).Collections, res1
@@ -94,9 +76,6 @@ func (set EndpointsSet) GetItem(arg0 context.Context, arg1 string, arg2 string,
 	}
 	response, res1 := set.GetItemEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetItemResponse).Item, res1
@@ -112,9 +91,6 @@ func (set EndpointsSet) FindItems(arg0 context.Context, arg1 string, arg2 string
 	}
 	response, res2 := set.FindItemsEndpoint(arg0, &request)
 	if res2 != nil {
-		if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res2 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*FindItemsResponse).Items, response.(*FindItemsResponse).Total, res2
@@ -130,9 +106,6 @@ func (set EndpointsSet) Aggregate(arg0 context.Context, arg1 string, arg2 string
 	}
 	response, res1 := set.AggregateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*AggregateResponse).Result, res1
diff --git a/pkg/delivery/transport/grpc/client.go b/pkg/delivery/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..18c8b75e6e68d0b71759c4bbfba3507c7bb78dfa
--- /dev/null
+++ b/pkg/delivery/transport/grpc/client.go
@@ -0,0 +1,24 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/delivery/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		AggregateEndpoint:        grpcerr.ClientMiddleware(c.AggregateEndpoint),
+		FindItemsEndpoint:        grpcerr.ClientMiddleware(c.FindItemsEndpoint),
+		GetCollectionEndpoint:    grpcerr.ClientMiddleware(c.GetCollectionEndpoint),
+		GetEnvironmentEndpoint:   grpcerr.ClientMiddleware(c.GetEnvironmentEndpoint),
+		GetItemEndpoint:          grpcerr.ClientMiddleware(c.GetItemEndpoint),
+		ListCollectionsEndpoint:  grpcerr.ClientMiddleware(c.ListCollectionsEndpoint),
+		ListEnvironmentsEndpoint: grpcerr.ClientMiddleware(c.ListEnvironmentsEndpoint),
+		ListLocalesEndpoint:      grpcerr.ClientMiddleware(c.ListLocalesEndpoint),
+	}
+}
diff --git a/pkg/delivery/transport/grpc/protobuf_type_converters.microgen.go b/pkg/delivery/transport/grpc/protobuf_type_converters.microgen.go
index 6961c709da405a34710e76ff6c1ad6f34c218f1b..7067dc51722117ddc00dd6208de6da9bd8a8e9bd 100644
--- a/pkg/delivery/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/delivery/transport/grpc/protobuf_type_converters.microgen.go
@@ -24,10 +24,10 @@ import (
 	"google.golang.org/protobuf/types/known/timestamppb"
 )
 
-func ListPtrLocalesLocaleToProto(locales []*locales.Locale) ([]*localespb.Locale, error) {
-	protoLocales := make([]*localespb.Locale, 0, len(locales))
-	for _, l := range locales {
-		protoLocales = append(protoLocales, &localespb.Locale{Id: l.ID, Name: l.Name, SpaceId: l.SpaceID})
+func ListPtrLocalesLocaleToProto(ls []*locales.Locale) ([]*localespb.Locale, error) {
+	protoLocales := make([]*localespb.Locale, 0, len(ls))
+	for _, l := range ls {
+		protoLocales = append(protoLocales, locales.LocaleToProto(l))
 	}
 	return protoLocales, nil
 }
@@ -35,7 +35,7 @@ func ListPtrLocalesLocaleToProto(locales []*locales.Locale) ([]*localespb.Locale
 func ProtoToListPtrLocalesLocale(protoLocales []*localespb.Locale) ([]*locales.Locale, error) {
 	ls := make([]*locales.Locale, 0, len(protoLocales))
 	for _, pl := range protoLocales {
-		ls = append(ls, &locales.Locale{ID: pl.Id, Name: pl.Name, SpaceID: pl.SpaceId})
+		ls = append(ls, locales.LocaleFromProto(pl))
 	}
 	return ls, nil
 }
@@ -190,33 +190,11 @@ func PtrItemsItemToProto(item *items.Item) (*itemspb.Item, error) {
 		return nil, nil
 	}
 
-	protoItem := &itemspb.Item{
-		Id:           item.ID,
-		SpaceId:      item.SpaceID,
-		EnvId:        item.EnvID,
-		CollectionId: item.CollectionID,
-		State:        itemspb.Item_State(item.State),
-		CreatedBy:    item.CreatedBy,
-		UpdatedBy:    item.UpdatedBy,
-		RevisionId:   item.RevisionID,
-		Locale:       item.Locale,
-		//Hidden, Template, Deleted - не передается для delivery
-	}
-
-	var err error
-	protoItem.Data, err = MapStringInterfaceToProto(item.Data)
-	if err != nil {
-		return nil, err
-	}
-	protoItem.Translations, err = MapStringMapStringInterfaceToProto(item.Translations)
-	if err != nil {
-		return nil, err
-	}
-	//protoItem.Permissions - не передается для delivery
-
-	protoItem.CreatedRevAt = timestamppb.New(item.CreatedRevAt)
-	protoItem.CreatedAt = timestamppb.New(item.CreatedAt)
-	protoItem.UpdatedAt = timestamppb.New(item.UpdatedAt)
+	protoItem := items.ItemToProto(item)
+	protoItem.Permissions = nil
+	protoItem.Hidden = false
+	protoItem.Template = false
+	protoItem.Deleted = false
 
 	return protoItem, nil
 }
@@ -226,26 +204,11 @@ func ProtoToPtrItemsItem(protoItem *itemspb.Item) (*items.Item, error) {
 		return nil, nil
 	}
 
-	item := &items.Item{
-		ID:           protoItem.Id,
-		SpaceID:      protoItem.SpaceId,
-		EnvID:        protoItem.EnvId,
-		CollectionID: protoItem.CollectionId,
-		State:        items.State(protoItem.State),
-		CreatedBy:    protoItem.CreatedBy,
-		UpdatedBy:    protoItem.UpdatedBy,
-		RevisionID:   protoItem.RevisionId,
-		Locale:       protoItem.Locale,
-		//Hidden, Template, Deleted - не передается для delivery
-	}
-
-	item.Data, _ = ProtoToMapStringInterface(protoItem.Data)
-	item.Translations, _ = ProtoToMapStringMapStringInterface(protoItem.Translations)
-	//item.Permissions - не передается для delivery
-
-	item.CreatedRevAt = protoItem.CreatedRevAt.AsTime()
-	item.CreatedAt = protoItem.CreatedAt.AsTime()
-	item.UpdatedAt = protoItem.UpdatedAt.AsTime()
+	item := items.ItemFromProto(protoItem)
+	item.Permissions = nil
+	item.Hidden = false
+	item.Template = false
+	item.Deleted = false
 
 	return item, nil
 }
diff --git a/pkg/delivery/transport/grpc/server.go b/pkg/delivery/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..e2db4e5642d8066364bec55b99427f4cdb8107f3
--- /dev/null
+++ b/pkg/delivery/transport/grpc/server.go
@@ -0,0 +1,24 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/delivery"
+	"git.perx.ru/perxis/perxis-go/pkg/delivery/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	pb "git.perx.ru/perxis/perxis-go/proto/delivery"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc delivery.Delivery, opts ...grpckit.ServerOption) pb.DeliveryServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		AggregateEndpoint:        grpcerr.ServerMiddleware(eps.AggregateEndpoint),
+		FindItemsEndpoint:        grpcerr.ServerMiddleware(eps.FindItemsEndpoint),
+		GetCollectionEndpoint:    grpcerr.ServerMiddleware(eps.GetCollectionEndpoint),
+		GetEnvironmentEndpoint:   grpcerr.ServerMiddleware(eps.GetEnvironmentEndpoint),
+		GetItemEndpoint:          grpcerr.ServerMiddleware(eps.GetItemEndpoint),
+		ListCollectionsEndpoint:  grpcerr.ServerMiddleware(eps.ListCollectionsEndpoint),
+		ListEnvironmentsEndpoint: grpcerr.ServerMiddleware(eps.ListEnvironmentsEndpoint),
+		ListLocalesEndpoint:      grpcerr.ServerMiddleware(eps.ListLocalesEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/environments/environment.go b/pkg/environments/environment.go
index e9d2b96e8337ac2ad94315b671aeea42e54c47cb..3e0f7bb62a0be1d2fbef334322a6cb315060af8a 100644
--- a/pkg/environments/environment.go
+++ b/pkg/environments/environment.go
@@ -6,6 +6,18 @@ const (
 	DefaultEnvironment = "master"
 )
 
+var (
+	ReadAllowedStates = []State{
+		StateNew,
+		StateReady,
+	}
+
+	WriteAllowedStates = []State{
+		StateNew,
+		StateReady,
+	}
+)
+
 type State int
 
 const (
@@ -91,4 +103,4 @@ func (e Environment) Clone() *Environment {
 	}
 
 	return clone
-}
\ No newline at end of file
+}
diff --git a/pkg/environments/events.go b/pkg/environments/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..169b0ac05ffb28ec4cf9722ec48f002e1a44536d
--- /dev/null
+++ b/pkg/environments/events.go
@@ -0,0 +1,8 @@
+package environments
+
+const (
+	EventCreate  = "environments.create"
+	EventUpdate  = "environments.update"
+	EventDelete  = "environments.delete"
+	EventMigrate = "environments.migrate"
+)
diff --git a/pkg/environments/middleware/caching_middleware.go b/pkg/environments/middleware/caching_middleware.go
index 0801730a0e93947c847352a9710a02f85ae94ee0..c53f45e8bdcdb61f59f1af0acf11bc0f412c2629 100644
--- a/pkg/environments/middleware/caching_middleware.go
+++ b/pkg/environments/middleware/caching_middleware.go
@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 	service "git.perx.ru/perxis/perxis-go/pkg/environments"
 )
 
@@ -30,7 +31,7 @@ func (m cachingMiddleware) Create(ctx context.Context, env *service.Environment)
 
 	environment, err = m.next.Create(ctx, env)
 	if err == nil {
-		m.cache.Remove(environment.SpaceID)
+		_ = m.cache.Remove(environment.SpaceID)
 	}
 	return environment, err
 }
@@ -39,29 +40,31 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId string, envId string
 
 	value, e := m.cache.Get(makeKey(spaceId, envId))
 	if e == nil {
-		return value.(*service.Environment), err
+		return value.(*service.Environment).Clone(), nil
 	}
 	environment, err = m.next.Get(ctx, spaceId, envId)
 	if err == nil {
-		m.cache.Set(makeKey(spaceId, environment.ID), environment)
+		_ = m.cache.Set(makeKey(spaceId, environment.ID), environment)
 		for _, a := range environment.Aliases {
-			m.cache.Set(makeKey(spaceId, a), environment)
+			_ = m.cache.Set(makeKey(spaceId, a), environment)
 		}
+		return environment.Clone(), nil
 	}
-	return environment, err
+	return nil, err
 }
 
 func (m cachingMiddleware) List(ctx context.Context, spaceId string) (environments []*service.Environment, err error) {
 
 	value, e := m.cache.Get(spaceId)
 	if e == nil {
-		return value.([]*service.Environment), err
+		return data.CloneSlice(value.([]*service.Environment)), nil
 	}
 	environments, err = m.next.List(ctx, spaceId)
 	if err == nil {
-		m.cache.Set(spaceId, environments)
+		_ = m.cache.Set(spaceId, environments)
+		return data.CloneSlice(environments), nil
 	}
-	return environments, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Update(ctx context.Context, env *service.Environment) (err error) {
@@ -71,12 +74,12 @@ func (m cachingMiddleware) Update(ctx context.Context, env *service.Environment)
 		value, e := m.cache.Get(makeKey(env.SpaceID, env.ID))
 		if e == nil {
 			env := value.(*service.Environment)
-			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 			for _, a := range env.Aliases {
-				m.cache.Remove(makeKey(env.SpaceID, a))
+				_ = m.cache.Remove(makeKey(env.SpaceID, a))
 			}
 		}
-		m.cache.Remove(env.SpaceID)
+		_ = m.cache.Remove(env.SpaceID)
 	}
 	return err
 }
@@ -88,12 +91,12 @@ func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, envId str
 		value, e := m.cache.Get(makeKey(spaceId, envId))
 		if e == nil {
 			env := value.(*service.Environment)
-			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 			for _, a := range env.Aliases {
-				m.cache.Remove(makeKey(env.SpaceID, a))
+				_ = m.cache.Remove(makeKey(env.SpaceID, a))
 			}
 		}
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
@@ -105,21 +108,21 @@ func (m cachingMiddleware) SetAlias(ctx context.Context, spaceId string, envId s
 		value, e := m.cache.Get(makeKey(spaceId, alias))
 		if e == nil {
 			env := value.(*service.Environment)
-			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 			for _, a := range env.Aliases {
-				m.cache.Remove(makeKey(env.SpaceID, a))
+				_ = m.cache.Remove(makeKey(env.SpaceID, a))
 			}
 		}
 
 		value, e = m.cache.Get(makeKey(spaceId, envId))
 		if e == nil {
 			env := value.(*service.Environment)
-			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 			for _, a := range env.Aliases {
-				m.cache.Remove(makeKey(env.SpaceID, a))
+				_ = m.cache.Remove(makeKey(env.SpaceID, a))
 			}
 		}
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
@@ -128,22 +131,22 @@ func (m cachingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envI
 
 	err = m.next.RemoveAlias(ctx, spaceId, envId, alias)
 	if err == nil {
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 		value, e := m.cache.Get(makeKey(spaceId, alias))
 		if e == nil {
 			env := value.(*service.Environment)
-			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 			for _, a := range env.Aliases {
-				m.cache.Remove(makeKey(env.SpaceID, a))
+				_ = m.cache.Remove(makeKey(env.SpaceID, a))
 			}
 		}
 
 		value, e = m.cache.Get(makeKey(spaceId, envId))
 		if e == nil {
 			env := value.(*service.Environment)
-			m.cache.Remove(makeKey(env.SpaceID, env.ID))
+			_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 			for _, a := range env.Aliases {
-				m.cache.Remove(makeKey(env.SpaceID, a))
+				_ = m.cache.Remove(makeKey(env.SpaceID, a))
 			}
 		}
 	}
@@ -157,11 +160,11 @@ func (m cachingMiddleware) Migrate(ctx context.Context, spaceId, envId string, o
 	value, e := m.cache.Get(makeKey(spaceId, envId))
 	if e == nil {
 		env := value.(*service.Environment)
-		m.cache.Remove(makeKey(env.SpaceID, env.ID))
+		_ = m.cache.Remove(makeKey(env.SpaceID, env.ID))
 		for _, a := range env.Aliases {
-			m.cache.Remove(makeKey(env.SpaceID, a))
+			_ = m.cache.Remove(makeKey(env.SpaceID, a))
 		}
 	}
-	m.cache.Remove(spaceId)
+	_ = m.cache.Remove(spaceId)
 	return err
 }
diff --git a/pkg/environments/middleware/caching_middleware_test.go b/pkg/environments/middleware/caching_middleware_test.go
index 784ce35632e2bcd74993ce499f4ec1db329041d7..cbac396352657115e96d98b6715aa7e371a353a8 100644
--- a/pkg/environments/middleware/caching_middleware_test.go
+++ b/pkg/environments/middleware/caching_middleware_test.go
@@ -40,11 +40,13 @@ func TestEnvironmentsCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, envID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша при повторном запросе по ID.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша при повторном запросе по ID.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.Get(ctx, spaceID, envAlias)
 		require.NoError(t, err)
-		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+		assert.Equal(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+		assert.NotSame(t, v3, v2)
 
 		envs.AssertExpectations(t)
 	})
@@ -61,11 +63,13 @@ func TestEnvironmentsCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, envAlias)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша по alias.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша по alias.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.Get(ctx, spaceID, envID)
 		require.NoError(t, err)
-		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+		assert.Equal(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+		assert.NotSame(t, v3, v2)
 
 		envs.AssertExpectations(t)
 	})
@@ -82,7 +86,8 @@ func TestEnvironmentsCache(t *testing.T) {
 
 		vl2, err := svc.List(ctx, spaceID)
 		require.NoError(t, err)
-		assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+		assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+		assert.NotSame(t, vl1[0], vl2[0])
 
 		envs.AssertExpectations(t)
 	})
@@ -102,14 +107,16 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			err = svc.SetAlias(ctx, spaceID, envID, envAlias)
 			require.NoError(t, err)
@@ -123,11 +130,12 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v5, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.Same(t, v4, v5, "Ожидается получение объекта из кэша по ID.")
+			assert.Equal(t, v4, v5, "Ожидается получение объекта из кэша по ID.")
+			assert.NotSame(t, v4, v5)
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
 
 			envs.AssertExpectations(t)
 		})
@@ -146,18 +154,21 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша по ID.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша по ID.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envAlias)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			err = svc.RemoveAlias(ctx, spaceID, envID, envAlias)
 			require.NoError(t, err)
@@ -172,11 +183,11 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v4, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v4, "Ожидает что элемент был удален из кеша и получен из сервиса по ID.")
+			assert.NotEqual(t, v3, v4, "Ожидает что элемент был удален из кеша и получен из сервиса по ID.")
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
 
 			envs.AssertExpectations(t)
 		})
@@ -195,18 +206,21 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envAlias)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			err = svc.Update(ctx, &environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}})
 			require.NoError(t, err)
@@ -219,15 +233,16 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v4, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v4, "Ожидает что элемент был удален из кэша и будет запрошен заново из сервиса.")
+			assert.NotEqual(t, v2, v4, "Ожидает что элемент был удален из кэша и будет запрошен заново из сервиса.")
 
 			v5, err := svc.Get(ctx, spaceID, envAlias)
 			require.NoError(t, err)
-			assert.Same(t, v4, v5, "Ожидается получение объекта из кэша по Alias после обновления объекта и получения по ID.")
+			assert.Equal(t, v4, v5, "Ожидается получение объекта из кэша по Alias после обновления объекта и получения по ID.")
+			assert.NotSame(t, v4, v5)
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
 
 			envs.AssertExpectations(t)
 		})
@@ -245,7 +260,8 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			err = svc.Update(ctx, &environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}})
 			require.NoError(t, err)
@@ -254,7 +270,7 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кэша и запрошены из сервиса.")
 
 			envs.AssertExpectations(t)
 		})
@@ -273,18 +289,21 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			v3, err := svc.Get(ctx, spaceID, envAlias)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша по Alias.")
+			assert.NotSame(t, v2, v3)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			err = svc.Delete(ctx, spaceID, envID)
 			require.NoError(t, err)
@@ -320,7 +339,8 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			envs.On("Create", mock.Anything, mock.Anything).Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once()
 			_, err = svc.Create(ctx, &environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"})
@@ -349,15 +369,16 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, "envID2")
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
-
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 			_, err = svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
 
 			v5, err := svc.Get(ctx, spaceID, "envID2")
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v5, "Ожидает что объект был удален из кэша и будет запрошен заново из сервиса.")
-
+			assert.Equal(t, v2, v5)
+			assert.NotSame(t, v2, v5)
 			envs.AssertExpectations(t)
 		})
 
@@ -372,7 +393,8 @@ func TestEnvironmentsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 
@@ -380,6 +402,8 @@ func TestEnvironmentsCache(t *testing.T) {
 			v3, err := svc.Get(ctx, spaceID, envID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидает что объект был удален из кэша и будет запрошен заново из сервиса.")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			envs.AssertExpectations(t)
 		})
diff --git a/pkg/environments/middleware/logging_middleware.go b/pkg/environments/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..2cb0112956df53f7d89c489427997e35ddad2c96
--- /dev/null
+++ b/pkg/environments/middleware/logging_middleware.go
@@ -0,0 +1,159 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   environments.Environments
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next environments.Environments) environments.Environments {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Environments")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, env)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(env))
+		return
+	}
+
+	logger.Info("Environment created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventDelete),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, envId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Environment deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	env, err = m.next.Get(ctx, spaceId, envId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return env, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	envs, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return envs, err
+}
+
+func (m *loggingMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventMigrate),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.Migrate(ctx, spaceId, envId, options...)
+	if err != nil {
+		logger.Error("Failed to migrate", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	// Информация о миграции будет отправлена в сервис логирования внутри самого сервиса.
+
+	return err
+}
+
+func (m *loggingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventUpdate),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.RemoveAlias(ctx, spaceId, envId, alias)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to remove alias '%s'", alias), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Environment alias '%s' removed", alias), logzap.Channels(logzap.Userlog))
+	return err
+}
+
+func (m *loggingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventUpdate),
+		logzap.Object(id.NewEnvironmentId(spaceId, envId)),
+	)
+
+	err = m.next.SetAlias(ctx, spaceId, envId, alias)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to set alias '%s'", alias), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Environment alias '%s' set", alias), logzap.Channels(logzap.Userlog))
+	return err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(environments.EventUpdate),
+		logzap.Object(env),
+	)
+
+	err = m.next.Update(ctx, env)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Environment updated", logzap.Channels(logzap.Userlog))
+	return err
+}
diff --git a/pkg/environments/middleware/middleware.go b/pkg/environments/middleware/middleware.go
index 7c887417848d7cdea79681b73d7c1954318ad009..d8e2816928241555275aa14a87f03d35ae99bcf7 100644
--- a/pkg/environments/middleware/middleware.go
+++ b/pkg/environments/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s environments.Environments, logger *zap.Logger, log_access bool) e
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/environments/service.go b/pkg/environments/service.go
index cd0a37f449819a4b92239f8d8b38a3a0036ec8d8..920fc043704f9d7dcf51d83032d98292be758550 100644
--- a/pkg/environments/service.go
+++ b/pkg/environments/service.go
@@ -2,6 +2,11 @@ package environments
 
 import (
 	"context"
+	"slices"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"github.com/avast/retry-go/v4"
 )
 
 // Environments
@@ -18,3 +23,51 @@ type Environments interface {
 	RemoveAlias(ctx context.Context, spaceId, envId, alias string) (err error)
 	Migrate(ctx context.Context, spaceId, envId string, options ...*MigrateOptions) (err error)
 }
+
+const (
+	RetryDelay         = 1 * time.Second
+	RetryAttempts uint = 1200
+)
+
+// IsAvailable проверяет доступность окружения.
+// Если окружение недоступно, возвращает ошибку ErrEnvironmentUnavailable
+func IsAvailable(ctx context.Context, svc Environments, spaceID string, environmentID string) error {
+	env, err := svc.Get(ctx, spaceID, environmentID)
+	if err != nil {
+		return err
+	}
+	if env.StateInfo == nil || slices.Contains(WriteAllowedStates, env.StateInfo.State) {
+		return nil
+	}
+	return errors.WithContext(ErrEnvironmentUnavailable, "state", env.StateInfo.State)
+}
+
+// WaitForAvailable периодически проверяет состояние окружения, пока оно не станет доступным.
+// Если окружение становится доступным, функция возвращает nil.
+// Если достигнуто максимальное количество попыток, функция возвращает ошибку.
+//
+// Через необязательные параметры можно, например, настроить задержку между попытками (по умолчанию равную RetryDelay) и максимальное количество попыток (по умолчанию равное RetryAttempts),
+// но опции retry.Context и retry.RetryIf будут проигнорированы.
+func WaitForAvailable(ctx context.Context, svc Environments, spaceID string, environmentID string, opts ...retry.Option) error {
+	opts = slices.Concat(
+		[]retry.Option{
+			retry.DelayType(retry.FixedDelay),
+			retry.Delay(RetryDelay),
+			retry.Attempts(RetryAttempts),
+		},
+		opts,
+		// Чтобы предотвратить изменение параметров пользователем, размещаем их в конце списка.
+		[]retry.Option{
+			retry.Context(ctx),
+			retry.RetryIf(func(err error) bool {
+				return errors.Is(err, ErrEnvironmentUnavailable)
+			}),
+		},
+	)
+	return retry.Do(
+		func() error {
+			return IsAvailable(ctx, svc, spaceID, environmentID)
+		},
+		opts...,
+	)
+}
diff --git a/pkg/environments/service_test.go b/pkg/environments/service_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9a3e6b06ec81e3ab8e84cefdfb20aec22c7213af
--- /dev/null
+++ b/pkg/environments/service_test.go
@@ -0,0 +1,111 @@
+package environments
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/avast/retry-go/v4"
+	"github.com/stretchr/testify/assert"
+)
+
+type dummyEnvironments struct {
+	Environments
+	environment *Environment
+	sleep       time.Duration
+}
+
+func (t *dummyEnvironments) Get(ctx context.Context, _ string, _ string) (*Environment, error) {
+	if t.sleep != 0 {
+		time.Sleep(t.sleep)
+		if err := ctx.Err(); err != nil {
+			return nil, err
+		}
+	}
+	return t.environment, nil
+}
+
+func TestIsAvailable(t *testing.T) {
+	tests := []struct {
+		name        string
+		environment *Environment
+		wantErr     bool
+	}{
+		{
+			"Environment has nil StateInfo: available",
+			&Environment{ID: "env-id", SpaceID: "space-id"},
+			false,
+		},
+		{
+			"Environment state is StateReady: available",
+			&Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StateReady}},
+			false,
+		},
+		{
+			"Environment state is StatePreparing: not available",
+			&Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StatePreparing}},
+			true,
+		},
+		{
+			"Environment state is StateError: not available",
+			&Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StateError}},
+			true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			environments := &dummyEnvironments{environment: tt.environment}
+			if err := IsAvailable(context.Background(), environments, "space-id", "master"); (err != nil) != tt.wantErr {
+				t.Errorf("IsAvailable() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestWaitForAvailable(t *testing.T) {
+	t.Run("Success", func(t *testing.T) {
+		env := &Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StatePreparing}}
+		environments := &dummyEnvironments{environment: env}
+
+		// Имитируем завершение подготовки окружения через 1 секунду
+		go func() {
+			time.Sleep(1 * time.Second)
+			env.StateInfo = &StateInfo{State: StateReady}
+		}()
+
+		err := WaitForAvailable(context.Background(), environments, "space-id", "master")
+		assert.NoError(t, err)
+	})
+	t.Run("Overwritten retry options", func(t *testing.T) {
+		env := &Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StatePreparing}}
+		environments := &dummyEnvironments{environment: env}
+
+		err := WaitForAvailable(context.Background(), environments, "space-id", "master", retry.Attempts(1))
+		assert.Error(t, err, "За одну попытку окружение не стало доступным, получаем ошибку")
+	})
+	t.Run("External context was canceled", func(t *testing.T) {
+		env := &Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StatePreparing}}
+		environments := &dummyEnvironments{environment: env}
+
+		ctx, cancel := context.WithCancel(context.Background())
+
+		// Отменяем контекст через 1 секунду
+		go func() {
+			time.Sleep(1 * time.Second)
+			cancel()
+		}()
+
+		err := WaitForAvailable(ctx, environments, "space-id", "master")
+		assert.ErrorIs(t, err, context.Canceled)
+	})
+	t.Run("External context was deadline exceeded", func(t *testing.T) {
+		env := &Environment{ID: "env-id", SpaceID: "space-id", StateInfo: &StateInfo{State: StatePreparing}}
+		environments := &dummyEnvironments{environment: env}
+
+		ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
+		defer cancel()
+
+		err := WaitForAvailable(ctx, environments, "space-id", "master")
+		assert.ErrorIs(t, err, context.DeadlineExceeded)
+	})
+}
diff --git a/pkg/environments/transport/client.microgen.go b/pkg/environments/transport/client.go
similarity index 66%
rename from pkg/environments/transport/client.microgen.go
rename to pkg/environments/transport/client.go
index 96094fda943e407d0264a8abc738fd5c3afe440e..d562acc24da13a6e0de05bedf6d78e419bdc2386 100644
--- a/pkg/environments/transport/client.microgen.go
+++ b/pkg/environments/transport/client.go
@@ -4,20 +4,14 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	environments "git.perx.ru/perxis/perxis-go/pkg/environments"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *environments.Environment) (res0 *environments.Environment, res1 error) {
 	request := CreateRequest{Env: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
@@ -30,9 +24,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Env, res1
@@ -42,9 +33,6 @@ func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*environ
 	request := ListRequest{SpaceId: arg1}
 	response, res1 := set.ListEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListResponse).Envs, res1
@@ -54,9 +42,6 @@ func (set EndpointsSet) Update(arg0 context.Context, arg1 *environments.Environm
 	request := UpdateRequest{Env: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -69,9 +54,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (
 	}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -85,9 +67,6 @@ func (set EndpointsSet) SetAlias(arg0 context.Context, arg1 string, arg2 string,
 	}
 	_, res0 = set.SetAliasEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -101,9 +80,6 @@ func (set EndpointsSet) RemoveAlias(arg0 context.Context, arg1 string, arg2 stri
 	}
 	_, res0 = set.RemoveAliasEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -117,9 +93,6 @@ func (set EndpointsSet) Migrate(arg0 context.Context, arg1 string, arg2 string,
 	}
 	_, res0 = set.MigrateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/environments/transport/grpc/client.go b/pkg/environments/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..c882782b3cb880317bc4a2e043f96c517136f3c3
--- /dev/null
+++ b/pkg/environments/transport/grpc/client.go
@@ -0,0 +1,24 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	transport "git.perx.ru/perxis/perxis-go/pkg/environments/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint:      grpcerr.ClientMiddleware(c.CreateEndpoint),
+		DeleteEndpoint:      grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		GetEndpoint:         grpcerr.ClientMiddleware(c.GetEndpoint),
+		ListEndpoint:        grpcerr.ClientMiddleware(c.ListEndpoint),
+		MigrateEndpoint:     grpcerr.ClientMiddleware(c.MigrateEndpoint),
+		RemoveAliasEndpoint: grpcerr.ClientMiddleware(c.RemoveAliasEndpoint),
+		SetAliasEndpoint:    grpcerr.ClientMiddleware(c.SetAliasEndpoint),
+		UpdateEndpoint:      grpcerr.ClientMiddleware(c.UpdateEndpoint),
+	}
+}
diff --git a/pkg/environments/transport/grpc/server.go b/pkg/environments/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..ed25b89f33790c7fedff28aed7868d2a6172af88
--- /dev/null
+++ b/pkg/environments/transport/grpc/server.go
@@ -0,0 +1,24 @@
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"git.perx.ru/perxis/perxis-go/pkg/environments/transport"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	pb "git.perx.ru/perxis/perxis-go/proto/environments"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc environments.Environments, opts ...grpckit.ServerOption) pb.EnvironmentsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint:      grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		DeleteEndpoint:      grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		GetEndpoint:         grpcerr.ServerMiddleware(eps.GetEndpoint),
+		ListEndpoint:        grpcerr.ServerMiddleware(eps.ListEndpoint),
+		MigrateEndpoint:     grpcerr.ServerMiddleware(eps.MigrateEndpoint),
+		RemoveAliasEndpoint: grpcerr.ServerMiddleware(eps.RemoveAliasEndpoint),
+		SetAliasEndpoint:    grpcerr.ServerMiddleware(eps.SetAliasEndpoint),
+		UpdateEndpoint:      grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/errors/code.go b/pkg/errors/code.go
index fdb94194b49508cc0f1e3610c41fc93d2c271bdf..08a87d477f66b2309ea58ad3a880a4f6e66dfdd5 100644
--- a/pkg/errors/code.go
+++ b/pkg/errors/code.go
@@ -30,7 +30,7 @@ func (e *codeError) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, e.Error())
+		_, _ = io.WriteString(s, e.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", e.Error())
 	}
diff --git a/pkg/errors/detail.go b/pkg/errors/detail.go
index a82ec3f8ad5fb7fd52f0ce0acdeeb833065e6ffa..556bf443ca81effa067748ac3f0f0e514d7e3665 100644
--- a/pkg/errors/detail.go
+++ b/pkg/errors/detail.go
@@ -27,7 +27,7 @@ func (w *withDetail) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/domain.go b/pkg/errors/domain.go
index 35642c34e9537d236a83092855089804164a90de..20e3a25fd95ad4ff7d0b307729e4701151d6d5d3 100644
--- a/pkg/errors/domain.go
+++ b/pkg/errors/domain.go
@@ -31,7 +31,7 @@ func (w *withDomain) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/field.go b/pkg/errors/field.go
index 891863f0471ab0215229f1eb848f8fb3822e1334..2c40756a65d4b7d3eb76fa907198e2fc9648ff83 100644
--- a/pkg/errors/field.go
+++ b/pkg/errors/field.go
@@ -31,7 +31,7 @@ func (w *withField) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/hint.go b/pkg/errors/hint.go
index f654d4024936cdf19e65b89aaca2fef3070fb5bb..f327cd0784d3dbf40266f706cdaaa86451489b61 100644
--- a/pkg/errors/hint.go
+++ b/pkg/errors/hint.go
@@ -27,7 +27,7 @@ func (w *withHint) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/id.go b/pkg/errors/id.go
index 9d9804882e0892c92998560b9be4ae7ec0fe1558..6483ced353452810b384bf8e76eb300700a168fd 100644
--- a/pkg/errors/id.go
+++ b/pkg/errors/id.go
@@ -29,7 +29,7 @@ func (w *withId) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/multi.go b/pkg/errors/multi.go
index d27392c7a96ec2a8b653699052d9cbf7ee679ba8..f804cbd4420a8a8bef4ae137242dc60bb390b9b2 100644
--- a/pkg/errors/multi.go
+++ b/pkg/errors/multi.go
@@ -35,7 +35,7 @@ func (w *withMultiError) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/source.go b/pkg/errors/source.go
index c43df751c063b7c2e419071e22812aa890074b42..dd5b6354aee8aea6a3ef47c6c47b8fd04c01c612 100644
--- a/pkg/errors/source.go
+++ b/pkg/errors/source.go
@@ -47,7 +47,7 @@ func (w *withSource) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/errors/status.go b/pkg/errors/status.go
index 04b8261296574fb72e5750edc100d8c2e805f9ff..952995a7147df6522b7b48243b2e9c39d71b7d8e 100644
--- a/pkg/errors/status.go
+++ b/pkg/errors/status.go
@@ -32,7 +32,7 @@ func (w *withStatusError) Format(s fmt.State, verb rune) {
 		}
 		fallthrough
 	case 's':
-		io.WriteString(s, w.Error())
+		_, _ = io.WriteString(s, w.Error())
 	case 'q':
 		fmt.Fprintf(s, "%q", w.Error())
 	}
diff --git a/pkg/expr/context.go b/pkg/expr/context.go
index 4049d96f5f62a94219b247b09a4337b474112224..ef653f65d527b737b1949ca9e63987e86b37ce3d 100644
--- a/pkg/expr/context.go
+++ b/pkg/expr/context.go
@@ -51,6 +51,6 @@ func WithEnvKV(ctx context.Context, kv ...interface{}) context.Context {
 }
 
 func GetEnv(ctx context.Context) map[string]interface{} {
-	ctx, c := getContext(ctx)
+	_, c := getContext(ctx)
 	return c.Env
 }
diff --git a/pkg/expr/format.go b/pkg/expr/format.go
index 730388d2d1e654549f8dae00332c6958efb26a65..647d4eba1a6df77e3eacbd76fd8e48749b59c67e 100644
--- a/pkg/expr/format.go
+++ b/pkg/expr/format.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 	"time"
 
-	"git.perx.ru/perxis/perxis-go/pkg/id"
+	"git.perx.ru/perxis/perxis-go/id"
 	"github.com/gosimple/slug"
 )
 
diff --git a/pkg/expr/mongo.go b/pkg/expr/mongo.go
index bb4187dda74adce707798781b77d23a648db0eb1..5a503a7ee76eaa7a7d0197b955868a298b5fad73 100644
--- a/pkg/expr/mongo.go
+++ b/pkg/expr/mongo.go
@@ -23,6 +23,7 @@ var geoTypes = map[string]string{
 type MongoExprConfig struct {
 	Env                map[string]any
 	IdentifierRenameFn func(s string) string
+	Visitors           []ast.Visitor
 	Ops                []expr.Option
 }
 
@@ -55,6 +56,7 @@ func convertToMongo(ctx context.Context, config *MongoExprConfig, tree *parser.T
 
 	env[EnvContextKey] = ctx
 	exprConfig := GetDefaultConfig(env)
+	exprConfig.Visitors = config.Visitors
 
 	for _, op := range config.Ops {
 		op(exprConfig)
@@ -62,13 +64,16 @@ func convertToMongo(ctx context.Context, config *MongoExprConfig, tree *parser.T
 
 	env = exprConfig.Env.(map[string]interface{})
 
-	if len(exprConfig.Visitors) >= 0 {
-		for _, v := range exprConfig.Visitors {
-			ast.Walk(&tree.Node, v)
-		}
+	c := &compiler{
+		tree:               tree,
+		env:                env,
+		config:             exprConfig,
+		identifierRenameFn: config.IdentifierRenameFn,
+		visitors:           config.Visitors,
+	}
+	for _, v := range c.visitors {
+		ast.Walk(&tree.Node, v)
 	}
-
-	c := &compiler{tree: tree, env: env, config: exprConfig, identifierRenameFn: config.IdentifierRenameFn}
 	v, ok := c.compile(tree.Node).(bson.M)
 	if !ok || v == nil {
 		return nil, fmt.Errorf("invalid expression")
@@ -81,6 +86,7 @@ type compiler struct {
 	tree               *parser.Tree
 	config             *conf.Config
 	identifierRenameFn func(string) string
+	visitors           []ast.Visitor
 }
 
 func (c *compiler) eval(node ast.Node) interface{} {
@@ -162,43 +168,43 @@ func (c *compiler) IdentifierNode(node *ast.IdentifierNode) string {
 
 func (c *compiler) IntegerNode(node *ast.IntegerNode) int {
 	return node.Value
-	//t := node.Type()
-	//if t == nil {
+	// t := node.Type()
+	// if t == nil {
 	//	c.emitPush(node.Value)
 	//	return
-	//}
+	// }
 	//
-	//switch t.Kind() {
-	//case reflect.Float32:
+	// switch t.Kind() {
+	// case reflect.Float32:
 	//	c.emitPush(float32(node.Value))
-	//case reflect.Float64:
+	// case reflect.Float64:
 	//	c.emitPush(float64(node.Value))
 	//
-	//case reflect.Int:
+	// case reflect.Int:
 	//	c.emitPush(int(node.Value))
-	//case reflect.Int8:
+	// case reflect.Int8:
 	//	c.emitPush(int8(node.Value))
-	//case reflect.Int16:
+	// case reflect.Int16:
 	//	c.emitPush(int16(node.Value))
-	//case reflect.Int32:
+	// case reflect.Int32:
 	//	c.emitPush(int32(node.Value))
-	//case reflect.Int64:
+	// case reflect.Int64:
 	//	c.emitPush(int64(node.Value))
 	//
-	//case reflect.Uint:
+	// case reflect.Uint:
 	//	c.emitPush(uint(node.Value))
-	//case reflect.Uint8:
+	// case reflect.Uint8:
 	//	c.emitPush(uint8(node.Value))
-	//case reflect.Uint16:
+	// case reflect.Uint16:
 	//	c.emitPush(uint16(node.Value))
-	//case reflect.Uint32:
+	// case reflect.Uint32:
 	//	c.emitPush(uint32(node.Value))
-	//case reflect.Uint64:
+	// case reflect.Uint64:
 	//	c.emitPush(uint64(node.Value))
 	//
-	//default:
+	// default:
 	//	c.emitPush(node.Value)
-	//}
+	// }
 }
 
 func (c *compiler) FloatNode(node *ast.FloatNode) float64 {
@@ -277,32 +283,32 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} {
 	case ">=":
 		return bson.M{c.identifier(node.Left): bson.M{"$gte": c.eval(node.Right)}}
 
-	//case "+":
+	// case "+":
 	//	c.compile(node.Left)
 	//	c.compile(node.Right)
 	//	c.emit(OpAdd)
 	//
-	//case "-":
+	// case "-":
 	//	c.compile(node.Left)
 	//	c.compile(node.Right)
 	//	c.emit(OpSubtract)
 	//
-	//case "*":
+	// case "*":
 	//	c.compile(node.Left)
 	//	c.compile(node.Right)
 	//	c.emit(OpMultiply)
 	//
-	//case "/":
+	// case "/":
 	//	c.compile(node.Left)
 	//	c.compile(node.Right)
 	//	c.emit(OpDivide)
 	//
-	//case "%":
+	// case "%":
 	//	c.compile(node.Left)
 	//	c.compile(node.Right)
 	//	c.emit(OpModulo)
 	//
-	//case "**":
+	// case "**":
 	//	c.compile(node.Left)
 	//	c.compile(node.Right)
 	//	c.emit(OpExponent)
@@ -454,26 +460,26 @@ func (c *compiler) CallNode(node *ast.CallNode) interface{} {
 		return bson.M{v: bson.M{"$regex": fmt.Sprintf(".*%s$", regexp.QuoteMeta(t)), "$options": "i"}}
 	}
 	panic("unsupported function")
-	//for _, arg := range node.Arguments {
+	// for _, arg := range node.Arguments {
 	//	c.compile(arg)
-	//}
-	//op := OpCall
-	//if node.Fast {
+	// }
+	// op := OpCall
+	// if node.Fast {
 	//	op = OpCallFast
-	//}
-	//c.emit(op, c.makeConstant(Call{Name: node.Name, Size: len(node.Arguments)})...)
+	// }
+	// c.emit(op, c.makeConstant(Call{Name: node.Name, Size: len(node.Arguments)})...)
 }
 
 func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	panic("unsupported builin node")
-	//switch node.Name {
-	//case "len":
+	// switch node.Name {
+	// case "len":
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpLen)
 	//	c.emit(OpRot)
 	//	c.emit(OpPop)
 	//
-	//case "all":
+	// case "all":
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
 	//	var loopBreak int
@@ -486,7 +492,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.patchJump(loopBreak)
 	//	c.emit(OpEnd)
 	//
-	//case "none":
+	// case "none":
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
 	//	var loopBreak int
@@ -500,7 +506,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.patchJump(loopBreak)
 	//	c.emit(OpEnd)
 	//
-	//case "any":
+	// case "any":
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
 	//	var loopBreak int
@@ -513,7 +519,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.patchJump(loopBreak)
 	//	c.emit(OpEnd)
 	//
-	//case "one":
+	// case "one":
 	//	count := c.makeConstant("count")
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
@@ -530,7 +536,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.emit(OpEqual)
 	//	c.emit(OpEnd)
 	//
-	//case "filter":
+	// case "filter":
 	//	count := c.makeConstant("count")
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
@@ -550,7 +556,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.emit(OpEnd)
 	//	c.emit(OpArray)
 	//
-	//case "map":
+	// case "map":
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
 	//	size := c.emitLoop(func() {
@@ -560,7 +566,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.emit(OpEnd)
 	//	c.emit(OpArray)
 	//
-	//case "count":
+	// case "count":
 	//	count := c.makeConstant("count")
 	//	c.compile(node.Arguments[0])
 	//	c.emit(OpBegin)
@@ -575,12 +581,12 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 	//	c.emit(OpLoad, count...)
 	//	c.emit(OpEnd)
 	//
-	//default:
+	// default:
 	//	panic(fmt.Sprintf("unknown builtin %v", node.Name))
-	//}
+	// }
 }
 
-//func (c *compiler) emitLoop(body func()) []byte {
+// func (c *compiler) emitLoop(body func()) []byte {
 //	i := c.makeConstant("i")
 //	size := c.makeConstant("size")
 //	array := c.makeConstant("array")
@@ -607,7 +613,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) interface{} {
 //	c.emit(OpPop)
 //
 //	return size
-//}
+// }
 
 func (c *compiler) ClosureNode(node *ast.ClosureNode) interface{} {
 	return c.compile(node.Node)
@@ -615,25 +621,25 @@ func (c *compiler) ClosureNode(node *ast.ClosureNode) interface{} {
 
 func (c *compiler) PointerNode(node *ast.PointerNode) interface{} {
 	panic("unsupported pointer node")
-	//c.emit(OpLoad, c.makeConstant("array")...)
-	//c.emit(OpLoad, c.makeConstant("i")...)
-	//c.emit(OpIndex)
+	// c.emit(OpLoad, c.makeConstant("array")...)
+	// c.emit(OpLoad, c.makeConstant("i")...)
+	// c.emit(OpIndex)
 }
 
 func (c *compiler) ConditionalNode(node *ast.ConditionalNode) interface{} {
 	panic("unsupported conditional node")
-	//c.compile(node.Cond)
-	//otherwise := c.emit(OpJumpIfFalse, c.placeholder()...)
+	// c.compile(node.Cond)
+	// otherwise := c.emit(OpJumpIfFalse, c.placeholder()...)
 	//
-	//c.emit(OpPop)
-	//c.compile(node.Exp1)
-	//end := c.emit(OpJump, c.placeholder()...)
+	// c.emit(OpPop)
+	// c.compile(node.Exp1)
+	// end := c.emit(OpJump, c.placeholder()...)
 	//
-	//c.patchJump(otherwise)
-	//c.emit(OpPop)
-	//c.compile(node.Exp2)
+	// c.patchJump(otherwise)
+	// c.emit(OpPop)
+	// c.compile(node.Exp2)
 	//
-	//c.patchJump(end)
+	// c.patchJump(end)
 }
 
 func (c *compiler) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) int {
@@ -642,28 +648,28 @@ func (c *compiler) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) int
 
 func (c *compiler) ArrayNode(node *ast.ArrayNode) interface{} {
 	panic("unsupported array node")
-	//for _, node := range node.Nodes {
+	// for _, node := range node.Nodes {
 	//	c.compile(node)
-	//}
+	// }
 	//
-	//c.emitPush(len(node.Nodes))
-	//c.emit(OpArray)
+	// c.emitPush(len(node.Nodes))
+	// c.emit(OpArray)
 }
 
 func (c *compiler) MapNode(node *ast.MapNode) interface{} {
 	panic("unsupported map node")
-	//for _, pair := range node.Pairs {
+	// for _, pair := range node.Pairs {
 	//	c.compile(pair)
-	//}
+	// }
 	//
-	//c.emitPush(len(node.Pairs))
-	//c.emit(OpMap)
+	// c.emitPush(len(node.Pairs))
+	// c.emit(OpMap)
 }
 
 func (c *compiler) PairNode(node *ast.PairNode) interface{} {
 	panic("unsupported pair node")
-	//c.compile(node.Key)
-	//c.compile(node.Value)
+	// c.compile(node.Key)
+	// c.compile(node.Value)
 }
 
 // handleLenNode получает узел AST и возвращает запрос для mongo,
@@ -731,6 +737,9 @@ func (c *compiler) handleTryNode(node *ast.CallNode) (result interface{}) {
 	if err != nil {
 		panic(err)
 	}
+	for _, v := range c.visitors {
+		ast.Walk(&tree.Node, v)
+	}
 	subcompiler := &compiler{tree: tree, env: c.env, config: c.config, identifierRenameFn: c.identifierRenameFn}
 	result = subcompiler.compile(tree.Node).(bson.M)
 	return
diff --git a/pkg/expr/mongo_test.go b/pkg/expr/mongo_test.go
index 9a7df77d00a8c006327b5b2645818a014d86d9b7..587f25769e4a850d29d00fff3c93c09c6e647eb5 100644
--- a/pkg/expr/mongo_test.go
+++ b/pkg/expr/mongo_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 	"time"
 
-	"git.perx.ru/perxis/perxis-go/pkg/id"
+	"git.perx.ru/perxis/perxis-go/id"
 	"github.com/expr-lang/expr"
 	"github.com/expr-lang/expr/ast"
 	"github.com/stretchr/testify/assert"
diff --git a/pkg/extension/action.go b/pkg/extension/action.go
index 55827d81e6c5b2237b1f2ee188f88293ea22c08b..feb19d9a7ec4b8f93b1749bf1bfeb3f4be18cc59 100644
--- a/pkg/extension/action.go
+++ b/pkg/extension/action.go
@@ -18,6 +18,7 @@ type (
 	ActionRequest struct {
 		Extension    string                  `json:"extension,omitempty"`
 		Action       string                  `json:"action"`
+		LocaleID     string                  `json:"locale_id,omitempty"`
 		SpaceID      string                  `json:"space_id,omitempty"`
 		EnvID        string                  `json:"env_id,omitempty"`
 		CollectionID string                  `json:"collection_id,omitempty"`
@@ -143,6 +144,7 @@ func ActionRequestToPB(req *ActionRequest) *pb.ActionRequest {
 	return &pb.ActionRequest{
 		Extension:    req.Extension,
 		Action:       req.Action,
+		LocaleId:     req.LocaleID,
 		SpaceId:      req.SpaceID,
 		EnvId:        req.EnvID,
 		CollectionId: req.CollectionID,
@@ -162,6 +164,7 @@ func ActionRequestFromPB(req *pb.ActionRequest) *ActionRequest {
 	return &ActionRequest{
 		Extension:    req.Extension,
 		Action:       req.Action,
+		LocaleID:     req.LocaleId,
 		SpaceID:      req.SpaceId,
 		EnvID:        req.EnvId,
 		CollectionID: req.CollectionId,
diff --git a/pkg/extension/extension.go b/pkg/extension/extension.go
index ae7878d56de53f423ea86c28fa2f263cef6cbd03..15b92676be86b1782eb0ca8e5feb98bf1f8ba186 100644
--- a/pkg/extension/extension.go
+++ b/pkg/extension/extension.go
@@ -3,6 +3,9 @@ package extension
 import (
 	"context"
 
+	"go.uber.org/zap"
+
+	"git.perx.ru/perxis/perxis-go"
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
@@ -17,30 +20,29 @@ const (
 	StateInstalled    = pb.State_INSTALLED
 	StateInProgress   = pb.State_IN_PROGRESS
 	StateFail         = pb.State_FAIL
-
-	MetadataKey = "extension"
+	MetadataKey       = "extension"
 )
 
 type (
-	InstallRequest   = pb.InstallRequest
-	CheckRequest     = pb.CheckRequest
-	UninstallRequest = pb.UninstallRequest
-
+	InstallRequest      = pb.InstallRequest
+	CheckRequest        = pb.CheckRequest
+	UninstallRequest    = pb.UninstallRequest
 	ExtensionDescriptor = pb.ExtensionDescriptor
 	State               = pb.State
 )
 
 var (
-	ErrStart = errors.New("start failed")
-	ErrStop  = errors.New("stop failed")
-
-	ErrInstall   = errors.New("install failed")
-	ErrUpdate    = errors.New("update failed")
-	ErrCheck     = errors.New("check failed")
-	ErrUninstall = errors.New("uninstall failed")
-
+	ErrStart            = errors.New("start failed")
+	ErrStop             = errors.New("stop failed")
+	ErrInstall          = errors.New("install failed")
+	ErrUpdate           = errors.New("update failed")
+	ErrCheck            = errors.New("check failed")
+	ErrUninstall        = errors.New("uninstall failed")
 	ErrNotInstalled     = errors.New("not installed")
 	ErrUnknownExtension = errors.New("unknown extension")
+
+	ManifestAssets   = perxis.NewAssets[*ExtensionDescriptor]()
+	ManifestFromFile = ManifestAssets.MustOneFrom
 )
 
 // Runnable описывает интерфейс сервиса с запуском и остановкой. Вызывается сервером расширений
@@ -75,7 +77,8 @@ func CheckInstalled(ctx context.Context, content *content.Content, spaceID, envI
 	return status.State == StateInstalled, nil
 }
 
-func isMetadataEqual(s1, s2 *schema.Schema) bool {
+// isSameExtension возвращает true, если значение метаданных для ключа расширения совпадает
+func isSameExtension(s1, s2 *schema.Schema) bool {
 	if s1.Metadata == nil && s2.Metadata == nil {
 		return true
 	}
@@ -85,13 +88,16 @@ func isMetadataEqual(s1, s2 *schema.Schema) bool {
 	return s1.Metadata[MetadataKey] == s2.Metadata[MetadataKey]
 }
 
-// UpdateCollectionStrategy В дополнение к стратегии по умолчанию делает проверку, что обновляемая
-// коллекция была установлена расширением. Если в метаданных схемы отсутствует специальный ключ `MetadataKey`,
-// это означает, что коллекция была создана пользователем и отношения к расширению не имеет - вернется ошибка.
-func UpdateCollectionStrategy(s *setup.Setup, exist, collection *collections.Collection) (*collections.Collection, bool, bool, error) {
-	if !s.IsForce() && !collection.IsView() && !exist.IsView() && !isMetadataEqual(collection.Schema, exist.Schema) {
-		return nil, false, false, errors.WithDetailf(collections.ErrAlreadyExists, "Коллекция с идентификатором '%s' "+
-			"уже существует. Удалите ее или вызовите установку расширения с флагом Force", collection.ID)
+// UpdateExtensionCollections возвращает опцию для обновления коллекций расширения
+func UpdateExtensionCollections() setup.CollectionOption {
+	return func(e *setup.Collection) {
+		next := e.UpdateFunc
+		e.UpdateFunc = func(s *setup.Setup, old, new *collections.Collection) (*collections.Collection, bool) {
+			if !s.IsForce() && !new.IsView() && !old.IsView() && !isSameExtension(new.Schema, old.Schema) {
+				s.Logger().Warn("Collection is already exists and not updated", zap.String("ID", new.ID))
+				return nil, false
+			}
+			return next(s, old, new)
+		}
 	}
-	return setup.DefaultUpdateCollectionStrategyFn(s, exist, collection)
 }
diff --git a/pkg/extension/extension_test.go b/pkg/extension/extension_test.go
index 4c552b394f7a67cc9de5bb4aa25e90d22fe6058a..1e5755e67fe17d02d6d7b7fbf239da49e402ceb0 100644
--- a/pkg/extension/extension_test.go
+++ b/pkg/extension/extension_test.go
@@ -3,8 +3,11 @@ package extension
 import (
 	"testing"
 
+	"go.uber.org/zap"
+
+	"go.uber.org/zap/zaptest/observer"
+
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
-	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
 	"git.perx.ru/perxis/perxis-go/pkg/setup"
@@ -45,69 +48,80 @@ func Test_isMetadataEqual(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			assert.Equalf(t, tt.want, isMetadataEqual(tt.s1, tt.s2), "isMetadataExtensionEqual(%v, %v)", tt.s1, tt.s2)
+			assert.Equalf(t, tt.want, isSameExtension(tt.s1, tt.s2), "isMetadataExtensionEqual(%v, %v)", tt.s1, tt.s2)
 		})
 	}
 }
 
-func TestDefaultUpdateCollectionStrategyFn(t *testing.T) {
+func TestDefaultUpdate_ExtensionCollections(t *testing.T) {
 	tests := []struct {
 		name       string
 		exist      *collections.Collection
 		collection *collections.Collection
 		force      bool
-		wantErr    func(err error)
+		update     bool
+		wantLogs   int
 	}{
 		{
-			name: "collection belongs to extension",
+			name: "Same extension",
 			exist: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env",
 				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
 			collection: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "new name",
 				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
-			wantErr: nil,
+			update: true,
+		},
+		{
+			name: "Other extension",
+			exist: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env",
+				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
+			collection: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "new name",
+				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-2")},
+			update:   false,
+			wantLogs: 1,
 		},
 		{
-			name: "collection belongs to another extension",
+			name: "Other extension force",
 			exist: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env",
 				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
 			collection: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "new name",
 				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-2")},
-			wantErr: func(err error) {
-				assert.ErrorIs(t, err, collections.ErrAlreadyExists)
-				assert.Equal(t, "Коллекция с идентификатором 'coll' уже существует. Удалите ее или "+
-					"вызовите установку расширения с флагом Force", errors.GetDetail(err))
-			},
+			force:  true,
+			update: true,
 		},
 		{
-			name: "collection was created by user",
+			name: "User collection",
 			exist: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env",
 				Schema: schema.New("name", field.String())},
 			collection: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "new name",
 				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
-			wantErr: func(err error) {
-				assert.ErrorIs(t, err, collections.ErrAlreadyExists)
-				assert.Equal(t, "Коллекция с идентификатором 'coll' уже существует. Удалите ее или "+
-					"вызовите установку расширения с флагом Force", errors.GetDetail(err))
-			},
+			update:   false,
+			wantLogs: 1,
 		},
 		{
-			name: "collection was created by user with force",
+			name: "User collection force",
 			exist: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env",
-				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
+				Schema: schema.New("name", field.String())},
 			collection: &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "new name",
 				Schema: schema.New("name", field.String()).WithMetadata("extension", "extension-1")},
-			force:   true,
-			wantErr: nil,
+			force:  true,
+			update: true,
 		},
 	}
+
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			set := setup.NewSetup(nil, "sp", "env", nil).WithForce(tt.force)
-			_, _, _, err := UpdateCollectionStrategy(set, tt.exist, tt.collection)
-			if tt.wantErr != nil {
-				tt.wantErr(err)
-				return
-			}
+			cfg := setup.NewConfig()
+			cfg.Collections.Add(tt.collection, UpdateExtensionCollections())
+
+			core, ob := observer.New(zap.DebugLevel)
+			logger := zap.New(core)
+			set := setup.NewSetup(nil, "sp", "env", logger).WithForce(tt.force)
+			_, update := cfg.Collections[0].UpdateFunc(set, tt.exist, tt.collection)
+
+			assert.Equal(t, tt.update, update)
+			logs := ob.TakeAll()
+			assert.Len(t, logs, tt.wantLogs)
+
 		})
 	}
 }
diff --git a/pkg/extension/manager.go b/pkg/extension/manager.go
index 9de5dc4e71c1160a7277181a64085ebf6ab86032..9d4def7ce9e6c24c8e9ac257066606e16db80afb 100644
--- a/pkg/extension/manager.go
+++ b/pkg/extension/manager.go
@@ -55,7 +55,7 @@ func (e *ExtensionConnector) Connect() error {
 		return nil
 	}
 
-	conn, err := grpc.Dial(
+	conn, err := grpc.NewClient(
 		e.Descriptor.Url,
 		grpc.WithTransportCredentials(insecure.NewCredentials()),
 		grpc.WithChainUnaryInterceptor(
diff --git a/pkg/extension/manager_client.go b/pkg/extension/manager_client.go
index d15d0dd0dd9eb53c5183f1faacc655c01dd494a1..3adbf804204de244552d42875edf1fdf95c06350 100644
--- a/pkg/extension/manager_client.go
+++ b/pkg/extension/manager_client.go
@@ -16,7 +16,7 @@ type ManagerClient struct {
 }
 
 func NewManagerClientWithAddr(addr string) (*ManagerClient, error) {
-	cc, err := grpc.Dial(addr,
+	cc, err := grpc.NewClient(addr,
 		grpc.WithTransportCredentials(insecure.NewCredentials()),
 		grpc.WithUnaryInterceptor(auth.PrincipalClientInterceptor()),
 	)
diff --git a/pkg/extension/schema.go b/pkg/extension/schema.go
index 521ebad6428dbd308228090469f369367e65068d..83a0f14275f552f4ee7a2f3eefd813e47789540d 100644
--- a/pkg/extension/schema.go
+++ b/pkg/extension/schema.go
@@ -63,7 +63,7 @@ func NewActionsCollection(spaceID, envID string) *collections.Collection {
 			SetDescription("Выполняется переход пользователя в пользовательском интерфейсе").
 			WithUI(&field.UI{Widget: "Checkbox"}),
 		"navigation_route", field.String().SetTitle("Путь в интерфейсе (Deprecated)"),
-	)
+	).SetSingleLocale(true)
 
 	// Includes
 	sch.SetIncludes(
@@ -124,7 +124,7 @@ func NewExtensionsCollection(spaceID, envID string) *collections.Collection {
 				validate.EnumOpt{Name: StateInProgress.String(), Value: float64(StateInProgress)},
 			),
 		).SetTitle("Состояние").WithUI(&field.UI{Widget: "Select"}),
-	)
+	).SetSingleLocale(true)
 
 	// UI
 	sch.Field.UI.ListView = &field.View{Options: map[string]interface{}{
diff --git a/pkg/extension/schema_test.go b/pkg/extension/schema_test.go
index 20648678a159b6703d05687530ba8e16ded4c8f2..4b4f235ae6a43bbb7e9c39a56b45c51b530d7103 100644
--- a/pkg/extension/schema_test.go
+++ b/pkg/extension/schema_test.go
@@ -47,6 +47,8 @@ func TestEqualSchema(t *testing.T) {
 		require.NoError(t, err)
 		s2 := schema.New()
 		err = json.Unmarshal(b, s2)
+		s1.ClearState()
+		s2.ClearState()
 		require.NoError(t, err)
 		require.Equal(t, s1.Field, s2.Field)
 	}
diff --git a/pkg/extension/service/extension.go b/pkg/extension/service/extension.go
index 45ed0524f855f888d7baf9befb9080392185dfc1..5d57e56c86190403cfe18a8084c902497280c993 100644
--- a/pkg/extension/service/extension.go
+++ b/pkg/extension/service/extension.go
@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/extension"
 	"git.perx.ru/perxis/perxis-go/pkg/roles"
@@ -29,7 +30,6 @@ type Extension struct {
 	setupFunc SetupFunc
 	Content   *content.Content
 	Logger    *zap.Logger
-	manager   extension.Manager
 	router    extension.ActionRouter
 
 	withClient bool
@@ -98,7 +98,7 @@ func (s *Extension) setupExtensionClient(set *setup.Setup, spaceID string) {
 	role := *s.role
 	role.SpaceID = spaceID
 
-	set.AddRole(&role, setup.DeleteRoleIfRemove())
+	set.AddRole(&role, setup.DeleteRoleIfRemoveFlag())
 
 	if s.client == nil {
 		s.client = &clients.Client{}
@@ -130,11 +130,15 @@ func (s *Extension) setupExtensionClient(set *setup.Setup, spaceID string) {
 		}
 	}
 
-	set.AddClient(&client, setup.OverwriteClient(), setup.DeleteClientIfRemove())
+	set.AddClient(&client, setup.OverwriteClient(), setup.DeleteClientIfRemoveFlag())
 }
 
 func (s *Extension) GetSetup(spaceID, envID string) (*setup.Setup, error) {
 	set := s.setupFunc(spaceID, envID)
+	set.Config.Collections.WithOptions(
+		func(c *collections.Collection) bool { return true },
+		setup.AddMetadata(extension.MetadataKey, s.GetName()),
+	)
 	if set.HasErrors() {
 		s.Logger.Error("Invalid setup config", zap.Errors("Errors", set.Errors()))
 		return nil, set.Error()
@@ -184,13 +188,13 @@ func (s *Extension) Action(ctx context.Context, in *extension.ActionRequest) (*e
 	// мы так и не договорились, но проверка появилась
 	// пусть решает каждое расширение само
 	//
-	//ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, ext)
-	//if err != nil {
+	// ok, err := extension.CheckInstalled(ctx, s.Content, in.SpaceId, in.EnvId, ext)
+	// if err != nil {
 	//	return nil, errors.Wrap(err, "check extension installed")
-	//}
-	//if !ok {
+	// }
+	// if !ok {
 	//	return nil, errors.New("extension not installed")
-	//}
+	// }
 
 	if url, err := extension.NewActionURL(in.Action); s.router != nil && err == nil && url != nil && s.isCorrectExtension(ctx, url, in) {
 		if h, err := s.router(ctx, url, in); err == nil && h != nil {
diff --git a/pkg/extension/service/registrar.go b/pkg/extension/service/registrar.go
index 8a096ef2047058e70f355ece9136069b7d94d3af..28da7d37d7e3d5fd29d3dfc51ec8af3ecc83707f 100644
--- a/pkg/extension/service/registrar.go
+++ b/pkg/extension/service/registrar.go
@@ -8,7 +8,6 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/extension"
 	retry "github.com/avast/retry-go/v4"
 	"go.uber.org/zap"
-	"google.golang.org/grpc"
 )
 
 const RegistrationDelay = time.Minute
@@ -16,12 +15,11 @@ const RegistrationDelay = time.Minute
 // Registrar выполняет действия по регистрации и обновления регистрации расширений в менеджере расширений. Одновременно
 // выполняется регистрация одного или нескольких расширений
 type Registrar struct {
-	addr        string
-	managerConn *grpc.ClientConn
-	manager     extension.Manager
-	exts        []extension.Extension
-	logger      *zap.Logger
-	stopFn      func() error
+	addr    string
+	manager extension.Manager
+	exts    []extension.Extension
+	logger  *zap.Logger
+	stopFn  func() error
 }
 
 func NewRegistrar(addr string, man extension.Manager, exts []extension.Extension, logger *zap.Logger) *Registrar {
@@ -86,7 +84,7 @@ func (reg *Registrar) Start() error {
 		for {
 			select {
 			case <-time.After(registrationDelay):
-				reg.register(regCtx, reg.manager, extList)
+				_ = reg.register(regCtx, reg.manager, extList)
 				registrationDelay = RegistrationDelay
 			case <-regCtx.Done():
 				reg.logger.Debug("Stop registration process")
diff --git a/pkg/files/downloader.go b/pkg/files/downloader.go
index 74aa91022906f794d07c314c5104011e4763caa9..2ec9f00d5feb2adaf7cf6a0d3305268b1f4f8717 100644
--- a/pkg/files/downloader.go
+++ b/pkg/files/downloader.go
@@ -50,7 +50,7 @@ func NewDummyDownloader() Downloader {
 
 func (d *dummyDownloader) Download(dst io.Writer, file *File) error {
 	// png pixel 10x10
-	pixel, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC")
-	_, err = dst.Write(pixel)
+	pixel, _ := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC")
+	_, err := dst.Write(pixel)
 	return err
 }
diff --git a/pkg/files/field.go b/pkg/files/field.go
index 5ce4dc4ce72506fdd62e184b95487319f95e5122..ffb315d5522da6995b7a9a81040f47f933310df1 100644
--- a/pkg/files/field.go
+++ b/pkg/files/field.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"io"
 	"net/url"
 	"reflect"
 
@@ -21,6 +22,27 @@ type FileParameters struct {
 
 func (p FileParameters) Type() field.Type                   { return p.t }
 func (p *FileParameters) Clone(reset bool) field.Parameters { return p }
+func (p FileParameters) GetField(f *field.Field, name string) *field.Field {
+	var fld *field.Field
+	switch name {
+	case "id", "name", "mimeType", "url", "key":
+		fld = field.String()
+	case "size":
+		fld = field.Number(field.NumberFormatInt)
+	}
+	return f.SetFieldState(name, fld)
+}
+
+func (p FileParameters) ListFields(f *field.Field, filter ...field.FieldFilterFunc) []*field.Field {
+	return []*field.Field{
+		f.SetFieldState("id", field.String()),
+		f.SetFieldState("name", field.String()),
+		f.SetFieldState("mimeType", field.String()),
+		f.SetFieldState("size", field.Number(field.NumberFormatInt)),
+		f.SetFieldState("url", field.String()),
+		f.SetFieldState("key", field.String()),
+	}
+}
 
 type FileType struct {
 	fs            Files
@@ -65,14 +87,21 @@ func (t FileType) Encode(ctx context.Context, fld *field.Field, v interface{}) (
 		return nil, fmt.Errorf("FileField encode error: incorrect type: \"%s\", expected \"file\"", reflect.ValueOf(v).Kind())
 	}
 
-	if f.File != nil { // upload from file system
+	if f.Content != nil {
+		// По возможности устанавливаем указатель в начало,
+		// для избегания ошибки при повторном чтении
+		if seeker, ok := f.Content.(io.Seeker); ok {
+			_, _ = seeker.Seek(0, io.SeekStart)
+		}
+
 		upload, err := t.fs.Upload(ctx, f)
 		if err != nil {
 			return nil, err
 		}
-		if err = t.uploader.Upload(f.File, upload); err != nil {
+		if err = t.uploader.Upload(f.Content, upload); err != nil {
 			return nil, err
 		}
+
 		f = &upload.File
 	}
 
@@ -137,17 +166,6 @@ func (t *FileType) IsEmpty(v interface{}) bool {
 	return !ok || f.ID == ""
 }
 
-func (p FileParameters) GetField(path string) (fld *field.Field) {
-	switch path {
-	case "id", "name", "mimeType", "url", "key":
-		return field.String()
-	case "size":
-		return field.Number(field.NumberFormatInt)
-	default:
-		return nil
-	}
-}
-
 func init() {
 	// По умолчанию без FS
 	// Если нужны подписанные URL, и загрузка на FS, нужно зарегистрировать корректный типа
diff --git a/pkg/files/field_test.go b/pkg/files/field_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7b6317309654c9027ca0b373b2362b7d3aad54af
--- /dev/null
+++ b/pkg/files/field_test.go
@@ -0,0 +1,34 @@
+package files
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFileField_Get(t *testing.T) {
+	sch := schema.New(
+		"a", field.String(),
+		"b", field.Object(
+			"file", Field(),
+		),
+		"file", Field(),
+	)
+	sch.ClearState()
+
+	assert.Equal(t, field.String(), sch.GetField("file.id"))
+	assert.Equal(t, field.String(), sch.GetField("file.name"))
+	assert.Equal(t, field.String(), sch.GetField("file.mimeType"))
+	assert.Equal(t, field.String(), sch.GetField("file.url"))
+	assert.Equal(t, field.String(), sch.GetField("file.key"))
+	assert.Equal(t, field.Number(field.NumberFormatInt), sch.GetField("file.size"))
+
+	assert.Equal(t, field.String(), sch.GetField("b.file.id"))
+	assert.Equal(t, field.String(), sch.GetField("b.file.name"))
+	assert.Equal(t, field.String(), sch.GetField("b.file.mimeType"))
+	assert.Equal(t, field.String(), sch.GetField("b.file.url"))
+	assert.Equal(t, field.String(), sch.GetField("b.file.key"))
+	assert.Equal(t, field.Number(field.NumberFormatInt), sch.GetField("b.file.size"))
+}
diff --git a/pkg/files/file.go b/pkg/files/file.go
index d2236b83368e3d5efc763fd6f7e53664e15cc827..ce49d8e227cd8a71dbd2fd948fb9c4e8cecc382f 100644
--- a/pkg/files/file.go
+++ b/pkg/files/file.go
@@ -3,11 +3,11 @@ package files
 import (
 	"bytes"
 	"fmt"
-	"io/fs"
+	"io"
 	"strings"
 	"text/template"
 
-	"git.perx.ru/perxis/perxis-go/pkg/id"
+	"git.perx.ru/perxis/perxis-go/id"
 )
 
 const (
@@ -16,13 +16,13 @@ const (
 
 // File - описание файла в системе хранения perxis
 type File struct {
-	ID       string  `mapstructure:"id,omitempty" json:"id" expr:"id"`                                              // Уникальный идентификатор файла в хранилище
-	Name     string  `mapstructure:"name,omitempty" json:"name" bson:"name,omitempty" expr:"name"`                  // Имя файла
-	Size     int     `mapstructure:"size,omitempty" json:"size" bson:"size,omitempty" expr:"size"`                  // Размер файла
-	MimeType string  `mapstructure:"mimeType,omitempty" json:"mimeType" bson:"mimeType,omitempty" expr:"mime_type"` // Mime-type файла
-	URL      string  `mapstructure:"url,omitempty" json:"url" bson:"url,omitempty" expr:"url"`                      // Адрес для загрузки файла
-	Key      string  `mapstructure:"key,omitempty" json:"key" bson:"key,omitempty" expr:"key"`                      // Ключ для хранения файла в хранилище
-	File     fs.File `mapstructure:"-" json:"-" bson:"-"`                                                           // Файл для загрузки(из файловой системы)
+	ID       string    `mapstructure:"id,omitempty" json:"id" expr:"id"`                                              // Уникальный идентификатор файла в хранилище
+	Name     string    `mapstructure:"name,omitempty" json:"name" bson:"name,omitempty" expr:"name"`                  // Имя файла
+	Size     uint64    `mapstructure:"size,omitempty" json:"size" bson:"size,omitempty" expr:"size"`                  // Размер файла
+	MimeType string    `mapstructure:"mimeType,omitempty" json:"mimeType" bson:"mimeType,omitempty" expr:"mime_type"` // Mime-type файла
+	URL      string    `mapstructure:"url,omitempty" json:"url" bson:"url,omitempty" expr:"url"`                      // Адрес для загрузки файла
+	Key      string    `mapstructure:"key,omitempty" json:"key" bson:"key,omitempty" expr:"key"`                      // Ключ для хранения файла в хранилище
+	Content  io.Reader `mapstructure:"-" json:"-" bson:"-"`                                                           // Альтернативный способ задать содержимое файла
 }
 
 func (f File) Clone() *File {
@@ -47,7 +47,7 @@ func (f *File) SetURLWithTemplate(t *template.Template) error {
 	return nil
 }
 
-func NewFile(name, mimeType string, size int, temp bool) *File {
+func NewFile(name, mimeType string, size uint64, temp bool) *File {
 	i := id.GenerateNewID()
 	if temp {
 		i = fmt.Sprintf("%s%s", TemporaryPrefix, i)
@@ -64,7 +64,7 @@ func NewFile(name, mimeType string, size int, temp bool) *File {
 type MultipartUpload struct {
 	File
 	UploadID string           `json:"upload_id"` // Идентификатор загрузки хранилища
-	PartSize int              `json:"part_size"` // Размер блока для загрузки
+	PartSize uint64           `json:"part_size"` // Размер блока для загрузки
 	PartURLs []string         `json:"part_urls"` // Адреса для загрузки полного файла
 	Parts    []*CompletedPart `json:"parts"`     // Идентификаторы загруженных блоков (S3 ETAGs)
 }
diff --git a/pkg/files/file_test.go b/pkg/files/file_test.go
index 617bed4c09ef95ab58874d856a36c5584abd4589..9a67b13c8861d4eaa75cee6cdaca8255e25be432 100644
--- a/pkg/files/file_test.go
+++ b/pkg/files/file_test.go
@@ -69,7 +69,7 @@ func TestFile_InExpr(t *testing.T) {
 	}{
 		{"f.id", map[string]interface{}{"f": &File{ID: "some_id"}}, "some_id", false},
 		{"f.name", map[string]interface{}{"f": &File{Name: "some_name"}}, "some_name", false},
-		{"f.size", map[string]interface{}{"f": &File{Size: 1}}, 1, false},
+		{"f.size", map[string]interface{}{"f": &File{Size: uint64(1)}}, uint64(1), false},
 		{"f.mime_type", map[string]interface{}{"f": &File{MimeType: "some_mime_type"}}, "some_mime_type", false},
 		{"f.url", map[string]interface{}{"f": &File{URL: "some_url"}}, "some_url", false},
 		{"f.key", map[string]interface{}{"f": &File{Key: "some_key"}}, "some_key", false},
diff --git a/pkg/files/transport/client.microgen.go b/pkg/files/transport/client.go
similarity index 66%
rename from pkg/files/transport/client.microgen.go
rename to pkg/files/transport/client.go
index 6320c479808103ebdfef774c51c85fa1ea5f9cfe..09bf05fece9262b9811ed3cb9d0323f9aeb1267d 100644
--- a/pkg/files/transport/client.microgen.go
+++ b/pkg/files/transport/client.go
@@ -4,20 +4,14 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	files "git.perx.ru/perxis/perxis-go/pkg/files"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) StartUpload(arg0 context.Context, arg1 *files.MultipartUpload) (res0 *files.MultipartUpload, res1 error) {
 	request := StartUploadRequest{Upload: arg1}
 	response, res1 := set.StartUploadEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*StartUploadResponse).U, res1
@@ -27,9 +21,6 @@ func (set EndpointsSet) CompleteUpload(arg0 context.Context, arg1 *files.Multipa
 	request := CompleteUploadRequest{Upload: arg1}
 	response, res1 := set.CompleteUploadEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CompleteUploadResponse).U, res1
@@ -39,9 +30,6 @@ func (set EndpointsSet) AbortUpload(arg0 context.Context, arg1 *files.MultipartU
 	request := AbortUploadRequest{Upload: arg1}
 	_, res0 = set.AbortUploadEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -51,9 +39,6 @@ func (set EndpointsSet) MoveUpload(arg0 context.Context, arg1 *files.MultipartUp
 	request := MoveUploadRequest{Upload: arg1}
 	response, res1 := set.MoveUploadEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*MoveUploadResponse).File, res1
@@ -63,9 +48,6 @@ func (set EndpointsSet) Upload(arg0 context.Context, arg1 *files.File) (res0 *fi
 	request := UploadRequest{File: arg1}
 	response, res1 := set.UploadEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*UploadResponse).U, res1
@@ -75,9 +57,6 @@ func (set EndpointsSet) GetFile(arg0 context.Context, arg1 *files.File) (res0 *f
 	request := GetFileRequest{File: arg1}
 	response, res1 := set.GetFileEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetFileResponse).F, res1
@@ -87,9 +66,6 @@ func (set EndpointsSet) DeleteFile(arg0 context.Context, arg1 *files.File) (res0
 	request := DeleteFileRequest{File: arg1}
 	_, res0 = set.DeleteFileEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/files/transport/grpc/client.go b/pkg/files/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..e528e995661c010eaf46206f003fcdbf43635810
--- /dev/null
+++ b/pkg/files/transport/grpc/client.go
@@ -0,0 +1,23 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/files/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		AbortUploadEndpoint:    grpcerr.ClientMiddleware(c.AbortUploadEndpoint),
+		CompleteUploadEndpoint: grpcerr.ClientMiddleware(c.CompleteUploadEndpoint),
+		DeleteFileEndpoint:     grpcerr.ClientMiddleware(c.DeleteFileEndpoint),
+		GetFileEndpoint:        grpcerr.ClientMiddleware(c.GetFileEndpoint),
+		MoveUploadEndpoint:     grpcerr.ClientMiddleware(c.MoveUploadEndpoint),
+		StartUploadEndpoint:    grpcerr.ClientMiddleware(c.StartUploadEndpoint),
+		UploadEndpoint:         grpcerr.ClientMiddleware(c.UploadEndpoint),
+	}
+}
diff --git a/pkg/files/transport/grpc/protobuf_type_converters.microgen.go b/pkg/files/transport/grpc/protobuf_type_converters.microgen.go
index 1240f9fc2e59251a6792fd04214455c6c75bb050..2bfdad9cc3df4dda0f30cbe7bf94941284a1ffe4 100644
--- a/pkg/files/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/files/transport/grpc/protobuf_type_converters.microgen.go
@@ -15,7 +15,7 @@ func PtrMultipartUploadToProto(upload *files.MultipartUpload) (*pb.MultipartUplo
 	}
 	pbUpload := &pb.MultipartUpload{
 		UploadId: upload.UploadID,
-		PartSize: int32(upload.PartSize),
+		PartSize: upload.PartSize,
 		PartUrls: upload.PartURLs,
 	}
 	pbUpload.Parts = make([]*pb.CompletedPart, 0, len(upload.Parts))
@@ -39,7 +39,7 @@ func ProtoToPtrMultipartUpload(protoUpload *pb.MultipartUpload) (*files.Multipar
 	}
 	upload := &files.MultipartUpload{
 		UploadID: protoUpload.UploadId,
-		PartSize: int(protoUpload.PartSize),
+		PartSize: protoUpload.PartSize,
 		PartURLs: protoUpload.PartUrls,
 	}
 	upload.Parts = make([]*files.CompletedPart, 0, len(protoUpload.Parts))
@@ -96,7 +96,7 @@ func PtrFileToProto(file *files.File) (*pb.File, error) {
 	pbFile := &pb.File{
 		Id:       file.ID,
 		Name:     file.Name,
-		Size:     int32(file.Size),
+		Size:     file.Size,
 		MimeType: file.MimeType,
 		Url:      file.URL,
 	}
@@ -110,7 +110,7 @@ func ProtoToPtrFile(protoFile *pb.File) (*files.File, error) {
 	file := &files.File{
 		ID:       protoFile.Id,
 		Name:     protoFile.Name,
-		Size:     int(protoFile.Size),
+		Size:     protoFile.Size,
 		MimeType: protoFile.MimeType,
 		URL:      protoFile.Url,
 	}
diff --git a/pkg/files/transport/grpc/server.go b/pkg/files/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..dcfc07598dbd6b2e0adb04d804e1cb8f223e55bf
--- /dev/null
+++ b/pkg/files/transport/grpc/server.go
@@ -0,0 +1,23 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/files"
+	"git.perx.ru/perxis/perxis-go/pkg/files/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/files"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc files.Files, opts ...grpckit.ServerOption) pb.FilesServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		AbortUploadEndpoint:    grpcerr.ServerMiddleware(eps.AbortUploadEndpoint),
+		CompleteUploadEndpoint: grpcerr.ServerMiddleware(eps.CompleteUploadEndpoint),
+		DeleteFileEndpoint:     grpcerr.ServerMiddleware(eps.DeleteFileEndpoint),
+		GetFileEndpoint:        grpcerr.ServerMiddleware(eps.GetFileEndpoint),
+		MoveUploadEndpoint:     grpcerr.ServerMiddleware(eps.MoveUploadEndpoint),
+		StartUploadEndpoint:    grpcerr.ServerMiddleware(eps.StartUploadEndpoint),
+		UploadEndpoint:         grpcerr.ServerMiddleware(eps.UploadEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/filter/filter.go b/pkg/filter/filter.go
index 04ebf36d2b95e7bc86018e5539a68d6fc1ddead9..d75dc99886698a14342dc4f5ad4d9423fc26d893 100644
--- a/pkg/filter/filter.go
+++ b/pkg/filter/filter.go
@@ -1,6 +1,7 @@
 package filter
 
 import (
+	"context"
 	"fmt"
 	"reflect"
 	"strings"
@@ -60,7 +61,7 @@ type FilterHandler struct {
 func NewFilterHandler(sch ...*schema.Schema) *FilterHandler {
 	return &FilterHandler{
 		schemas: sch,
-		//qbuilder: qb,
+		// qbuilder: qb,
 	}
 }
 
@@ -77,9 +78,7 @@ func (h *FilterHandler) removeFieldPrefix(f string) string {
 }
 
 func (h *FilterHandler) AddSchema(sch ...*schema.Schema) *FilterHandler {
-	for _, s := range sch {
-		h.schemas = append(h.schemas, s)
-	}
+	h.schemas = append(h.schemas, sch...)
 	return h
 }
 
@@ -129,7 +128,7 @@ func (h *FilterHandler) validate(sch *schema.Schema, f *Filter) (err error) {
 		if f.Value, err = schema.Decode(nil, fld, f.Value); err != nil {
 			return h.formatErr(f.Field, f.Op, err)
 		}
-		if err = validate.Validate(nil, fld, f.Value); err != nil {
+		if err = validate.Validate(context.Background(), fld, f.Value); err != nil {
 			return h.formatErr(f.Field, f.Op, err)
 		}
 	case In, NotIn:
@@ -377,7 +376,7 @@ func (b *mongoQueryBuilder) field(f string) string {
 }
 
 // $text search ??
-//func (b *mongoQueryBuilder) textSearchQuery(filters ...*Filter) string {
+// func (b *mongoQueryBuilder) textSearchQuery(filters ...*Filter) string {
 //	cnt, notcnt := "", ""
 //	for _, f := range filters {
 //		val, ok := f.Value.(string)
@@ -407,4 +406,4 @@ func (b *mongoQueryBuilder) field(f string) string {
 //		cnt += " " + notcnt
 //	}
 //	return cnt
-//}
+// }
diff --git a/pkg/invitations/events.go b/pkg/invitations/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..622f744796d5a24cac0bc078a48d62a6ba36a522
--- /dev/null
+++ b/pkg/invitations/events.go
@@ -0,0 +1,7 @@
+package invitations
+
+const (
+	EventCreate = "invitations.create"
+	EventDelete = "invitations.delete"
+	EventAccept = "invitations.accept"
+)
diff --git a/pkg/invitations/invitation.go b/pkg/invitations/invitation.go
index 5dc5913774fc5774076823309abdf52b195c87c2..a5b91ed006693748deb71877b8e555e485b0c6c8 100644
--- a/pkg/invitations/invitation.go
+++ b/pkg/invitations/invitation.go
@@ -14,3 +14,16 @@ type Invitation struct {
 	CreatedAt  *time.Time `bson:"createdAt"`
 	ValidUntil *time.Time `bson:"validUntil"`
 }
+
+func (i Invitation) Clone() *Invitation {
+	return &Invitation{
+		ID:         i.ID,
+		Email:      i.Email,
+		OrgID:      i.OrgID,
+		SpaceID:    i.SpaceID,
+		OwnerID:    i.OwnerID,
+		Role:       i.Role,
+		CreatedAt:  i.CreatedAt,
+		ValidUntil: i.ValidUntil,
+	}
+}
diff --git a/pkg/invitations/middleware/caching_middleware.go b/pkg/invitations/middleware/caching_middleware.go
index 5f2a1086a15c09d55b5461e029036b091d68dcfe..b14a155dc04022baed930f4252ec63caf47dab3b 100644
--- a/pkg/invitations/middleware/caching_middleware.go
+++ b/pkg/invitations/middleware/caching_middleware.go
@@ -30,20 +30,21 @@ func (m cachingMiddleware) Get(ctx context.Context, invitationId string) (inv *s
 
 	value, e := m.cache.Get(invitationId)
 	if e == nil {
-		return value.(*service.Invitation), err
+		return value.(*service.Invitation).Clone(), nil
 	}
 	inv, err = m.next.Get(ctx, invitationId)
 	if err == nil {
-		m.cache.Set(invitationId, inv)
+		_ = m.cache.Set(invitationId, inv)
+		return inv.Clone(), nil
 	}
-	return inv, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Accept(ctx context.Context, invitationId string, userId string) (err error) {
 
 	err = m.next.Accept(ctx, invitationId, userId)
 	if err == nil {
-		m.cache.Remove(invitationId)
+		_ = m.cache.Remove(invitationId)
 	}
 	return err
 }
@@ -56,7 +57,7 @@ func (m cachingMiddleware) Delete(ctx context.Context, invitationId string) (err
 
 	err = m.next.Delete(ctx, invitationId)
 	if err == nil {
-		m.cache.Remove(invitationId)
+		_ = m.cache.Remove(invitationId)
 	}
 	return err
 }
diff --git a/pkg/invitations/middleware/caching_middleware_test.go b/pkg/invitations/middleware/caching_middleware_test.go
index e7fac6f545463644ddd7476a43123f248b9a16b8..46d0a44342822a877be1feffbc396e37606e8157 100644
--- a/pkg/invitations/middleware/caching_middleware_test.go
+++ b/pkg/invitations/middleware/caching_middleware_test.go
@@ -41,7 +41,8 @@ func TestLocalesCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, invID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+		assert.Equal(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+		assert.NotSame(t, v1, v2)
 
 		inv.AssertExpectations(t)
 	})
@@ -59,7 +60,8 @@ func TestLocalesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, invID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			inv.On("Accept", mock.Anything, invID, usrID).Return(nil).Once()
 			inv.On("Get", mock.Anything, invID).Return(nil, errNotFound).Once()
@@ -86,7 +88,8 @@ func TestLocalesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, invID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			inv.On("Delete", mock.Anything, invID).Return(nil).Once()
 			inv.On("Get", mock.Anything, invID).Return(nil, errNotFound).Once()
@@ -113,7 +116,8 @@ func TestLocalesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, invID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается что при повторном запросе объект будет получен из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 
@@ -122,6 +126,8 @@ func TestLocalesCache(t *testing.T) {
 			v3, err := svc.Get(ctx, invID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается что при истечении ttl кеш будет очищен..")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			inv.AssertExpectations(t)
 		})
diff --git a/pkg/invitations/middleware/logging_middleware.go b/pkg/invitations/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..f32fa01b32887a2ced9b2a702d9e8434e1fef3de
--- /dev/null
+++ b/pkg/invitations/middleware/logging_middleware.go
@@ -0,0 +1,119 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   invitations.Invitations
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next invitations.Invitations) invitations.Invitations {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Invitations")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, invitation *invitations.Invitation) (created *invitations.Invitation, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(invitations.EventCreate),
+		logzap.Object(id.NewSpaceId(invitation.SpaceID)),
+	)
+
+	created, err = m.next.Create(ctx, invitation)
+	if err != nil {
+		logger.Error("Failed to create invitation", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Created invitation '%s'", created.ID), logzap.Channels(logzap.Userlog))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, invitationId string) (invitation *invitations.Invitation, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	invitation, err = m.next.Get(ctx, invitationId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return invitation, err
+}
+
+func (m *loggingMiddleware) Accept(ctx context.Context, invitationId, userId string) (err error) {
+	invitation, err := m.next.Get(ctx, invitationId)
+	if err != nil {
+		return errors.Wrap(err, "failed to get invitation")
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(invitations.EventAccept),
+		logzap.Object(id.NewSpaceId(invitation.SpaceID)),
+	)
+
+	err = m.next.Accept(ctx, invitationId, userId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to accept invitation '%s'", invitationId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Invitation '%s' accepted by user '%s'", invitationId, userId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *invitations.Filter, opts *options.FindOptions) (invitations []*invitations.Invitation, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	invitations, total, err = m.next.Find(ctx, filter, opts)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return invitations, total, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, invitationId string) (err error) {
+	invitation, err := m.next.Get(ctx, invitationId)
+	if err != nil {
+		return errors.Wrap(err, "failed to get invitation")
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(invitations.EventDelete),
+		logzap.Object(id.NewSpaceId(invitation.SpaceID)),
+	)
+
+	err = m.next.Delete(ctx, invitationId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to delete invitation '%s' ", invitationId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Invitation '%s' deleted", invitationId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/invitations/middleware/middleware.go b/pkg/invitations/middleware/middleware.go
index f59604ba948ade1b4fe03bc3e3cadd8de6f7cfcd..47a2d84b987fcd760086ff6837cd25867945e574 100644
--- a/pkg/invitations/middleware/middleware.go
+++ b/pkg/invitations/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s invitations.Invitations, logger *zap.Logger, log_access bool) inv
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/invitations/transport/client.microgen.go b/pkg/invitations/transport/client.go
similarity index 67%
rename from pkg/invitations/transport/client.microgen.go
rename to pkg/invitations/transport/client.go
index 6f896b0e67fa88a1eaa539c305a47fada11c9c8b..04ad55679775497da0ec7919450c8e8d54738a90 100644
--- a/pkg/invitations/transport/client.microgen.go
+++ b/pkg/invitations/transport/client.go
@@ -4,21 +4,15 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	invitations "git.perx.ru/perxis/perxis-go/pkg/invitations"
 	"git.perx.ru/perxis/perxis-go/pkg/options"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *invitations.Invitation) (res0 *invitations.Invitation, res1 error) {
 	request := CreateRequest{Invitation: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
@@ -28,9 +22,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string) (res0 *invitation
 	request := GetRequest{InvitationId: arg1}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Invitation, res1
@@ -43,9 +34,6 @@ func (set EndpointsSet) Accept(arg0 context.Context, arg1 string, arg2 string) (
 	}
 	_, res0 = set.AcceptEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -58,9 +46,6 @@ func (set EndpointsSet) Find(arg0 context.Context, arg1 *invitations.Filter, arg
 	}
 	response, res2 := set.FindEndpoint(arg0, &request)
 	if res2 != nil {
-		if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res2 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*FindResponse).Invitations, response.(*FindResponse).Total, res2
@@ -70,9 +55,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string) (res0 error) {
 	request := DeleteRequest{InvitationId: arg1}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/invitations/transport/grpc/client.go b/pkg/invitations/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..995dc563cc84f24b6f470de43531c6a6e62b90b2
--- /dev/null
+++ b/pkg/invitations/transport/grpc/client.go
@@ -0,0 +1,21 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/invitations/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		AcceptEndpoint: grpcerr.ClientMiddleware(c.AcceptEndpoint),
+		CreateEndpoint: grpcerr.ClientMiddleware(c.CreateEndpoint),
+		DeleteEndpoint: grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		FindEndpoint:   grpcerr.ClientMiddleware(c.FindEndpoint),
+		GetEndpoint:    grpcerr.ClientMiddleware(c.GetEndpoint),
+	}
+}
diff --git a/pkg/invitations/transport/grpc/server.go b/pkg/invitations/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..f0a23fcd1d4f21c3f0dde9b72e7cee1757ace8b4
--- /dev/null
+++ b/pkg/invitations/transport/grpc/server.go
@@ -0,0 +1,21 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/invitations/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/invitations"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc invitations.Invitations, opts ...grpckit.ServerOption) pb.InvitationsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		AcceptEndpoint: grpcerr.ServerMiddleware(eps.AcceptEndpoint),
+		CreateEndpoint: grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		DeleteEndpoint: grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		FindEndpoint:   grpcerr.ServerMiddleware(eps.FindEndpoint),
+		GetEndpoint:    grpcerr.ServerMiddleware(eps.GetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/items/errors.go b/pkg/items/errors.go
index 2d6cc1cfb5bb047ccd1ec96bc073bb4de600ccef..9ade59dbbd11ce3c9fc1624f1c02a044b57e53a4 100644
--- a/pkg/items/errors.go
+++ b/pkg/items/errors.go
@@ -7,8 +7,10 @@ import (
 )
 
 var (
-	ErrAccessDenied          = service.ErrAccessDenied
-	ErrNotFound              = service.ErrNotFound
-	ErrUniqueValueRequired   = errors.New("unique value required")
-	ErrTextSearchNotAvaluble = errors.New("fulltext search is not available in this collection")
+	ErrAccessDenied           = service.ErrAccessDenied
+	ErrNotFound               = service.ErrNotFound
+	ErrUniqueValueRequired    = errors.New("unique value required")
+	ErrTextSearchNotAvailable = errors.New("fulltext search is not available in this collection")
+
+	ErrCreateLocalizedItem = errors.New("create only supports default locale")
 )
diff --git a/pkg/items/item.go b/pkg/items/item.go
index c6fd3ad14cbe731c9d5f592f05d33cc2e7f5081b..d8271aae51369cde38e41121d211bda1a483695f 100644
--- a/pkg/items/item.go
+++ b/pkg/items/item.go
@@ -3,13 +3,14 @@ package items
 import (
 	"context"
 	"fmt"
-	"reflect"
 	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/data"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
 	"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/localizer"
 	pb "git.perx.ru/perxis/perxis-go/proto/items"
 	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
@@ -19,6 +20,7 @@ var (
 	ErrNotSystemField = errors.New("not a system field")
 	ErrIncorrectValue = errors.New("incorrect value")
 	ErrIncorrectField = errors.New("incorrect field")
+	ErrReservedField  = errors.New("cannot use reserved field name")
 )
 
 type State int
@@ -72,10 +74,12 @@ var SystemFields = []string{
 	"revision_description",
 	"data",
 	"translations",
-	"locale",
+	"translations_ids",
+	"locale_id",
 	"deleted",
 	"hidden",
 	"template",
+	"search_score",
 }
 
 type Permissions struct {
@@ -87,24 +91,44 @@ type Permissions struct {
 }
 
 type Item struct {
-	ID                  string                            `json:"id" bson:"_id"` // ID - Идентификатор записи. Автоматически генерируется системой при сохранении первой ревизии.
-	SpaceID             string                            `json:"spaceId" bson:"-"`
-	EnvID               string                            `json:"envId" bson:"-"`
-	CollectionID        string                            `json:"collectionId" bson:"-"`
-	State               State                             `json:"state" bson:"state"`
-	CreatedRevAt        time.Time                         `json:"createdRevAt,omitempty" bson:"created_rev_at,omitempty"`
-	CreatedBy           string                            `json:"createdBy,omitempty" bson:"created_by,omitempty"`
-	CreatedAt           time.Time                         `json:"createdAt,omitempty" bson:"created_at,omitempty"`
-	UpdatedAt           time.Time                         `json:"updatedAt,omitempty" bson:"updated_at,omitempty"`
-	UpdatedBy           string                            `json:"updatedBy,omitempty" bson:"updated_by,omitempty"`
-	Data                map[string]interface{}            `json:"data" bson:"data"`
-	Locale              string                            `json:"locale" bson:"-"`
-	Translations        map[string]map[string]interface{} `json:"translations" bson:"translations,omitempty"`
-	RevisionID          string                            `json:"revId,omitempty" bson:"revision_id"`
-	RevisionDescription string                            `json:"revDescription,omitempty" bson:"revision_description"`
-	Permissions         *Permissions                      `json:"permissions,omitempty" bson:"-"`
-
-	// Флаги записи
+	ID           string                 `json:"id" bson:"_id"` // ID - Идентификатор записи. Автоматически генерируется системой при сохранении первой ревизии.
+	SpaceID      string                 `json:"spaceId" bson:"-"`
+	EnvID        string                 `json:"envId" bson:"-"`
+	CollectionID string                 `json:"collectionId" bson:"-"`
+	State        State                  `json:"state" bson:"state"`
+	CreatedRevAt time.Time              `json:"createdRevAt,omitempty" bson:"created_rev_at,omitempty"`
+	CreatedBy    string                 `json:"createdBy,omitempty" bson:"created_by,omitempty"`
+	CreatedAt    time.Time              `json:"createdAt,omitempty" bson:"created_at,omitempty"`
+	UpdatedAt    time.Time              `json:"updatedAt,omitempty" bson:"updated_at,omitempty"`
+	UpdatedBy    string                 `json:"updatedBy,omitempty" bson:"updated_by,omitempty"`
+	Data         map[string]interface{} `json:"data" bson:"data"`
+
+	// При создании или обновлении идентификатор локали в котором создается запись, опционально.
+	// Если указан, то создается перевод для указанного языка, поле translations игнорируется
+	LocaleID string `json:"locale_id" bson:"-"`
+
+	// Позволяет одновременно установить/получить несколько переводов и производить манипуляции с переводами
+	// Ключами является идентификатор локали, значениями - данные переводы
+	// При обновлении не происходит валидация или модификация каждого из переводов в соответствие со схемой,
+	// поэтому обновление через поле `translations` стоит выполнять с аккуратностью
+	// Для удаления переводов реализована следующая логика:
+	// - {"lang":nil|{}} - сброс перевода для языка
+	// - {"lang":map{...}} - установка перевода для языка
+	// - {"lang":map{...}, "*":nil} - установка перевода для языка, сброс остальных переводов
+	// - {"*":nil} - сброс всех переводов
+	Translations map[string]map[string]interface{} `json:"translations" bson:"translations,omitempty"`
+
+	// Список идентификаторов локалей, для которых есть переводы.
+	// Соответствует ключам в translations
+	TranslationsIDs []string `json:"translations_ids" bson:"translations_ids,omitempty"`
+
+	RevisionID          string       `json:"revId,omitempty" bson:"revision_id"`
+	RevisionDescription string       `json:"revDescription,omitempty" bson:"revision_description"`
+	Permissions         *Permissions `json:"permissions,omitempty" bson:"-"`
+
+	// Релеватность элемента при полнотекстовом поиске
+	SearchScore float64 `json:"searchScore,omitempty" bson:"search_score,omitempty"`
+
 	Deleted  bool `json:"deleted" bson:"deleted,omitempty"`
 	Hidden   bool `json:"hidden" bson:"hidden,omitempty"`
 	Template bool `json:"template" bson:"template,omitempty"`
@@ -123,6 +147,11 @@ func NewItem(spaceID, envID, collID, id string, data map[string]interface{}, tra
 	}
 }
 
+// GetID возвращает идентификатор записи
+func (i *Item) GetID() string {
+	return i.ID
+}
+
 func (i *Item) Clone() *Item {
 	itm := *i
 	itm.Data = data.CloneMap(i.Data)
@@ -152,142 +181,185 @@ func (i *Item) ToMap() map[string]interface{} {
 		"revision_id":          i.RevisionID,
 		"revision_description": i.RevisionDescription,
 		"data":                 i.Data,
+		"locale_id":            i.LocaleID,
 		"translations":         i.Translations,
-		"locale":               i.Locale,
+		"translations_ids":     i.TranslationsIDs,
 		"deleted":              i.Deleted,
 		"hidden":               i.Hidden,
 		"template":             i.Template,
+		"search_score":         i.SearchScore,
 	}
 }
 
-func (i *Item) SetData(locale string, data map[string]interface{}) {
-	if locale != "" {
-		if i.Translations == nil {
-			i.Translations = make(map[string]map[string]interface{})
-		}
-		i.Translations[locale] = data
+// SetData устанавливает перевод в нужное поле записи
+func (i *Item) SetData(dt map[string]interface{}, localeID string) {
+	if localeID == "" || localeID == locales.DefaultID {
+		i.Data = dt
 		return
 	}
-	i.Data = data
+	if i.Translations == nil {
+		i.Translations = map[string]map[string]interface{}{localeID: dt}
+		if !data.Contains(localeID, i.TranslationsIDs) {
+			i.TranslationsIDs = append(i.TranslationsIDs, localeID)
+		}
+	}
+	return
 }
 
-func (i *Item) GetData(locale string) map[string]interface{} {
-	if locale != "" && i.Translations != nil {
-		translation, _ := i.Translations[locale]
-		return MergeData(i.Data, translation)
+// GetData возвращает полные локализованные данные записи
+func (i *Item) GetData(localeID string) map[string]interface{} {
+	if localeID == "" || localeID == locales.DefaultID {
+		return i.Data
 	}
-	return i.Data
+	if i.Translations != nil {
+		return i.Translations[localeID]
+	}
+	return nil
 }
 
-func (i Item) Encode(ctx context.Context, s *schema.Schema) (*Item, error) {
+func (i *Item) AddTranslations(translations map[string]map[string]interface{}) {
+	if i.Translations == nil {
+		i.Translations = make(map[string]map[string]interface{}, len(translations))
+	}
+	for l, t := range translations {
+		i.Translations[l] = t
+	}
+	for k := range translations {
+		if !data.Contains(k, i.TranslationsIDs) {
+			i.TranslationsIDs = append(i.TranslationsIDs, k)
+		}
+	}
+}
+
+func (i *Item) Localize(localizer *localizer.Localizer) (err error) {
+	if localizer == nil {
+		return nil
+	}
+	i.Data, err = localizer.Localize(i.Data, i.Translations)
+	if err != nil {
+		return err
+	}
+	i.LocaleID = localizer.LocaleID()
+	i.Translations = nil
+	return nil
+}
+
+// // UnsetTranslation устанавливает значение перевода в nil, для сброса перевода для языка
+// // "localeID":map{...}, "*":nil} - установка перевода для языка, сброс остальных переводов
+// // {"*":nil} -сброс всех переводов
+// func (i *Item) UnsetTranslation(localeID string) {
+// 	if i.Translations == nil {
+// 		i.Translations = make(map[string]map[string]interface{})
+// 	}
+//
+// 	i.Translations[localeID] = nil
+// }
+//
+// // SetTranslation устанавливает перевод для языка
+// func (i *Item) SetTranslation(dt map[string]interface{}, localeID string) {
+// 	if i.Translations == nil {
+// 		i.Translations = make(map[string]map[string]interface{})
+// 	}
+//
+// 	i.Translations[localeID] = dt
+// }
+
+func (i *Item) Encode(ctx context.Context, s *schema.Schema) (*Item, error) {
+	res := *i
 	if i.Data != nil {
-		dt, err := schema.Encode(nil, s, i.Data)
+		dt, err := schema.Encode(ctx, s, i.Data)
 		if err != nil {
-			//return errors.WithField(err, "data")
+			// return errors.WithField(err, "data")
 			return nil, err
 		}
-		i.Data = dt.(map[string]interface{})
+		res.Data = dt.(map[string]interface{})
 	}
 	if len(i.Translations) > 0 {
+		res.Translations = make(map[string]map[string]interface{}, len(i.Translations))
 		for l, v := range i.Translations {
-			dt, err := schema.Encode(nil, s, v)
+			if len(v) == 0 {
+				res.Translations[l] = v
+				continue
+			}
+			dt, err := schema.Encode(ctx, s, v)
 			if err != nil {
-				//return errors.WithField(err, fmt.Sprintf("translations.%s", l))
 				return nil, err
 			}
-			i.Translations[l] = dt.(map[string]interface{})
+			res.Translations[l] = dt.(map[string]interface{})
 		}
 	}
-	return &i, nil
+	return &res, nil
 }
 
-func (i Item) Decode(ctx context.Context, s *schema.Schema) (res *Item, err error) {
-
+func (i *Item) Decode(ctx context.Context, s *schema.Schema) (*Item, error) {
+	res := *i
+	var err error
 	if i.Data != nil {
-		i.Data, err = s.Decode(ctx, i.Data)
+		res.Data, err = s.Decode(ctx, i.Data)
 		if err != nil {
 			return nil, err
-			//return errors.WithField(err, "data")
 		}
 	}
-
-	return &i, nil
-}
-
-// MergeData дополняет отсутствующие данные из оригинальных данных
-func MergeData(data ...map[string]interface{}) map[string]interface{} {
-	merge := make(map[string]interface{})
-	for _, d := range data {
-		for k, v := range d {
-			merge[k] = v
-		}
-	}
-	return merge
-}
-
-// ClearData убирает данные которые не изменились по сравнению с оригинальными данными
-func ClearData(data ...map[string]interface{}) map[string]interface{} {
-	var clear map[string]interface{}
-
-	for _, d := range data {
-		if clear == nil {
-			clear = d
-			continue
-		}
-
-		for k, v := range d {
-			if reflect.DeepEqual(clear[k], v) {
-				delete(clear, k)
+	if len(i.Translations) > 0 {
+		res.Translations = make(map[string]map[string]interface{}, len(i.Translations))
+		for l, v := range i.Translations {
+			if len(v) == 0 {
+				res.Translations[l] = v
+				continue
+			}
+			dt, err := schema.Decode(ctx, s, v)
+			if err != nil {
+				return nil, err
 			}
+			res.Translations[l] = dt.(map[string]interface{})
 		}
 	}
-
-	return clear
+	return &res, nil
 }
 
 type ProcessDataFunc func(ctx context.Context, sch *schema.Schema, data map[string]interface{}) (map[string]interface{}, error)
 
-func (i Item) ProcessData(ctx context.Context, sch *schema.Schema, fn ProcessDataFunc, locales ...string) (*Item, error) {
+func (i *Item) ProcessData(ctx context.Context, sch *schema.Schema, fn ProcessDataFunc, locales ...*locales.Locale) (*Item, error) {
+	res := *i
 	if i.Data != nil {
-		dt, err := fn(ctx, sch, i.Data)
+		dt, err := fn(ctx, sch, res.Data)
 		if err != nil {
 			return nil, errors.WithField(err, "data")
 		}
-		i.Data = dt
+		res.Data = dt
 	}
 
 	tr := make(map[string]map[string]interface{})
 	for _, l := range locales {
-
-		data := i.GetData(l)
-
-		dt, err := fn(ctx, sch, data)
+		itm := *i
+		err := itm.Localize(localizer.NewLocalizer(localizer.Config{Schema: sch, Locales: locales, LocaleID: l.ID}))
 		if err != nil {
-			return nil, errors.WithField(err, fmt.Sprintf("translations.%s", l))
+			return nil, errors.WithField(err, fmt.Sprintf("translations.%s", l.ID))
 		}
-		tr[l] = dt
 
+		dt, err := fn(ctx, sch, itm.Data)
+		if err != nil {
+			return nil, errors.WithField(err, fmt.Sprintf("translations.%s", l.ID))
+		}
+		tr[l.ID] = dt
 	}
 
-	i.Translations = nil
+	res.Translations = nil
 	if len(tr) > 0 {
-		i.Translations = tr
+		res.Translations = tr
 	}
 
-	return &i, nil
+	return &res, nil
 }
 
 // IsSystemField возвращает являться ли поле системным
 func IsSystemField(field string) bool {
-	if data.Contains(field, SystemFields) {
-		return true
-	}
-	return false
+	return data.Contains(field, SystemFields)
 }
 
 // SetSystemField устанавливает значение системного поля
 func (i *Item) SetSystemField(field string, value interface{}) error {
-	ok := true
+	var ok bool
 	switch field {
 	case "id":
 		i.ID, ok = value.(string)
@@ -317,6 +389,8 @@ func (i *Item) SetSystemField(field string, value interface{}) error {
 		i.Deleted, ok = value.(bool)
 	case "template":
 		i.Template, ok = value.(bool)
+	case "search_score":
+		i.SearchScore, ok = value.(float64)
 	default:
 		return ErrNotSystemField
 	}
@@ -328,7 +402,7 @@ func (i *Item) SetSystemField(field string, value interface{}) error {
 	return nil
 }
 
-// GetSystem устанавливает значение системного поля
+// GetSystem возвращает значение системного поля
 func (i *Item) GetSystem(field string) (any, error) {
 	switch field {
 	case "id":
@@ -359,6 +433,11 @@ func (i *Item) GetSystem(field string) (any, error) {
 		return i.Deleted, nil
 	case "template":
 		return i.Template, nil
+	case "search_score":
+		return i.SearchScore, nil
+	case "state":
+		return i.State, nil
+
 	}
 
 	return nil, ErrNotSystemField
@@ -421,6 +500,10 @@ func GetSystemField(fld string) (*field.Field, error) {
 		return field.String(), nil
 	case "hidden", "deleted", "template":
 		return field.Bool(), nil
+	case "search_score":
+		return field.Number(field.NumberFormatFloat), nil
+	case "state":
+		return field.Number(field.NumberFormatInt), nil
 	}
 
 	return nil, ErrNotSystemField
@@ -467,10 +550,12 @@ func ItemToProto(item *Item) *pb.Item {
 		UpdatedBy:           item.UpdatedBy,
 		RevisionId:          item.RevisionID,
 		RevisionDescription: item.RevisionDescription,
-		Locale:              item.Locale,
+		LocaleId:            item.LocaleID,
+		TranslationsIds:     item.TranslationsIDs,
 		Hidden:              item.Hidden,
 		Template:            item.Template,
 		Deleted:             item.Deleted,
+		SearchScore:         item.SearchScore,
 	}
 
 	if item.Data != nil {
@@ -479,7 +564,11 @@ func ItemToProto(item *Item) *pb.Item {
 	if item.Translations != nil {
 		protoItem.Translations = make(map[string]*structpb.Struct, len(item.Translations))
 		for k, v := range item.Translations {
-			protoItem.Translations[k], _ = structpb.NewStruct(v)
+			var t *structpb.Struct
+			if v != nil {
+				t, _ = structpb.NewStruct(v)
+			}
+			protoItem.Translations[k] = t
 		}
 	}
 
@@ -516,10 +605,12 @@ func ItemFromProto(protoItem *pb.Item) *Item {
 		UpdatedBy:           protoItem.UpdatedBy,
 		RevisionID:          protoItem.RevisionId,
 		RevisionDescription: protoItem.RevisionDescription,
-		Locale:              protoItem.Locale,
+		LocaleID:            protoItem.LocaleId,
+		TranslationsIDs:     protoItem.TranslationsIds,
 		Hidden:              protoItem.Hidden,
 		Template:            protoItem.Template,
 		Deleted:             protoItem.Deleted,
+		SearchScore:         protoItem.SearchScore,
 	}
 
 	if protoItem.Data != nil {
@@ -528,8 +619,16 @@ func ItemFromProto(protoItem *pb.Item) *Item {
 
 	if protoItem.Translations != nil {
 		item.Translations = make(map[string]map[string]interface{}, len(protoItem.Translations))
+	}
+	if protoItem.Translations != nil {
 		for k, v := range protoItem.Translations {
-			item.Translations[k] = v.AsMap()
+			// При proto.Marshal/Unmarshal `nil`-значение превращается в `Struct{}`, поэтому логика
+			// много смысла не несет, но хотя бы здесь сохраним различие пустого и nil значений
+			var t map[string]interface{}
+			if v != nil {
+				t = v.AsMap()
+			}
+			item.Translations[k] = t
 		}
 	}
 
diff --git a/pkg/items/item_test.go b/pkg/items/item_test.go
index dfcc16ee1c7b40441b5df54acc48a12b276669ca..05baa60a3c1e8eaae9534fe9026d7456d78ab36c 100644
--- a/pkg/items/item_test.go
+++ b/pkg/items/item_test.go
@@ -1,6 +1,7 @@
 package items
 
 import (
+	"context"
 	"fmt"
 	"testing"
 	"time"
@@ -8,19 +9,20 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func TestItem_Set(t *testing.T) {
 	item := &Item{}
 
-	item.Set("id", "id")
+	_ = item.Set("id", "id")
 	assert.Equal(t, "id", item.ID)
 	now := time.Now()
 
-	item.Set("created_at", now)
+	_ = item.Set("created_at", now)
 	assert.Equal(t, now, item.CreatedAt)
 
-	item.Set("a.b.c", 101)
+	_ = item.Set("a.b.c", 101)
 	assert.Equal(t, map[string]any{"a": map[string]any{"b": map[string]any{"c": 101}}}, item.Data)
 
 }
@@ -70,6 +72,7 @@ func TestGetField(t *testing.T) {
 		),
 		"arr", field.Array(field.Object("a", field.Time())),
 	)
+	sch.ClearState()
 
 	tests := []struct {
 		name    string
@@ -95,3 +98,169 @@ func TestGetField(t *testing.T) {
 		})
 	}
 }
+
+func TestItem_Proto(t *testing.T) {
+	w := time.Now().UTC()
+	tests := []struct {
+		name string
+		item *Item
+	}{
+		{
+			name: "All fields are filled",
+			item: &Item{
+				ID:           "id-1",
+				SpaceID:      "space-1",
+				EnvID:        "env-1",
+				CollectionID: "coll-1",
+				State:        StateDraft,
+				CreatedRevAt: w.Add(time.Hour),
+				CreatedBy:    "user-1",
+				CreatedAt:    w,
+				UpdatedAt:    w.Add(time.Hour),
+				UpdatedBy:    "user-2",
+				Data:         map[string]any{"a": "b", "c": "d", "x": nil},
+				LocaleID:     "ru",
+				Translations: map[string]map[string]interface{}{
+					"ru": {"a": "B"},
+					"en": nil,
+				},
+				TranslationsIDs:     []string{"ru", "en"},
+				RevisionID:          "rev-1",
+				RevisionDescription: "desc-1",
+				Permissions:         PermissionsAllowAny,
+				SearchScore:         100.0,
+				Deleted:             false,
+				Hidden:              true,
+				Template:            false,
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			assert.Equal(t, tc.item, ItemFromProto(ItemToProto(tc.item)))
+		})
+	}
+}
+
+func TestItem_Encode_Decode(t *testing.T) {
+	w := time.Now().UTC()
+	tests := []struct {
+		name string
+		item *Item
+	}{
+		{
+			name: "Data",
+			item: &Item{
+				ID:           "id-1",
+				SpaceID:      "space-1",
+				EnvID:        "env-1",
+				CollectionID: "coll-1",
+				State:        StateDraft,
+				CreatedRevAt: w.Add(time.Hour),
+				CreatedBy:    "user-1",
+				CreatedAt:    w,
+				UpdatedAt:    w.Add(time.Hour),
+				UpdatedBy:    "user-2",
+				Data: map[string]any{
+					"a": "text-a",
+					"b": 124.1,
+					"c": map[string]interface{}{"x": "y"},
+					"d": []interface{}{"k", "l", "m"},
+				},
+				RevisionID:          "rev-1",
+				RevisionDescription: "desc-1",
+				Permissions:         PermissionsAllowAny,
+				SearchScore:         100.0,
+				Deleted:             false,
+				Hidden:              true,
+				Template:            false,
+			},
+		},
+		{
+			name: "Data and Translations",
+			item: &Item{
+				ID:           "id-1",
+				SpaceID:      "space-1",
+				EnvID:        "env-1",
+				CollectionID: "coll-1",
+				State:        StateDraft,
+				CreatedRevAt: w.Add(time.Hour),
+				CreatedBy:    "user-1",
+				CreatedAt:    w,
+				UpdatedAt:    w.Add(time.Hour),
+				UpdatedBy:    "user-2",
+				Data: map[string]any{
+					"a": "text-a",
+					"b": 124.1,
+					"c": map[string]interface{}{"x": "y"},
+					"d": []interface{}{"k", "l", "m"},
+				},
+				LocaleID: "ru",
+				Translations: map[string]map[string]interface{}{
+					"ru": {"a": "ru-a"},
+					"en": {"a": "en-a"},
+				},
+				TranslationsIDs:     []string{"ru", "en"},
+				RevisionID:          "rev-1",
+				RevisionDescription: "desc-1",
+				Permissions:         PermissionsAllowAny,
+				SearchScore:         100.0,
+				Deleted:             false,
+				Hidden:              false,
+				Template:            false,
+			},
+		},
+		{
+			name: "Nil Translation",
+			item: &Item{
+				ID:           "id-1",
+				SpaceID:      "space-1",
+				EnvID:        "env-1",
+				CollectionID: "coll-1",
+				State:        StateDraft,
+				CreatedRevAt: w.Add(time.Hour),
+				CreatedBy:    "user-1",
+				CreatedAt:    w,
+				UpdatedAt:    w.Add(time.Hour),
+				UpdatedBy:    "user-2",
+				Data: map[string]any{
+					"a": "text-a",
+					"b": 124.1,
+					"c": map[string]interface{}{"x": "y"},
+					"d": []interface{}{"k", "l", "m"},
+				},
+				LocaleID: "ru",
+				Translations: map[string]map[string]interface{}{
+					"ru": {"a": "ru-a"},
+					"en": nil,
+				},
+				TranslationsIDs:     []string{"ru", "en"},
+				RevisionID:          "rev-1",
+				RevisionDescription: "desc-1",
+				Permissions:         PermissionsAllowAny,
+				SearchScore:         100.0,
+				Deleted:             false,
+				Hidden:              false,
+				Template:            false,
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			ctx := context.Background()
+			sch := schema.New(
+				"a", field.String(),
+				"b", field.Number(field.NumberFormatFloat),
+				"c", field.Object("x", field.String()),
+				"d", field.Array(field.String()),
+			)
+			enc, err := tc.item.Encode(ctx, sch)
+			require.NoError(t, err)
+			dec, err := enc.Decode(ctx, sch)
+			require.NoError(t, err)
+			assert.Equal(t, tc.item, dec)
+		})
+	}
+}
diff --git a/pkg/items/middleware/caching_middleware.go b/pkg/items/middleware/caching_middleware.go
index 6b11851381fb9dc13b630477052f05d4148bc765..60d3bf80250843f2ddc963fe5d242e58f590a3f7 100644
--- a/pkg/items/middleware/caching_middleware.go
+++ b/pkg/items/middleware/caching_middleware.go
@@ -6,7 +6,9 @@ import (
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
 	envService "git.perx.ru/perxis/perxis-go/pkg/environments"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	service "git.perx.ru/perxis/perxis-go/pkg/items"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
 )
 
 func makeKey(ss ...string) string {
@@ -32,145 +34,141 @@ type cachingMiddleware struct {
 }
 
 func (m cachingMiddleware) Get(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*service.GetOptions) (itm *service.Item, err error) {
+	opts := service.MergeGetOptions(options...)
+	localeID := opts.LocaleID
+	if localeID == "" {
+		localeID = locales.DefaultID
+	}
 
-	value, e := m.cache.Get(makeKey(spaceId, envId, collectionId, itemId))
-	if e == nil {
-		return value.(*service.Item), err
+	// Значение из кэша можно достать только в случае, когда не запрашиваются переводы. Для
+	// списка переводов (`item.Translations`) кэш не предусмотрен: предполагается, что это
+	// нечастый запрос и содержание кэша с разными переводами себя не оправдывает
+	var value = make(map[string]*service.Item)
+	if len(opts.TranslationsIDs) == 0 {
+		val, e := m.cache.Get(makeKey(spaceId, envId, collectionId, itemId))
+		if e == nil {
+			value = val.(map[string]*service.Item)
+			if i, ok := value[localeID]; ok {
+				return i.Clone(), nil
+			}
+		}
 	}
+
 	itm, err = m.Items.Get(ctx, spaceId, envId, collectionId, itemId, options...)
 	if err == nil {
 		env, err := m.envs.Get(ctx, itm.SpaceID, itm.EnvID)
 		if err != nil {
-			return nil, err
+			return nil, errors.Wrap(err, "get environment")
 		}
-		m.cache.Set(makeKey(itm.SpaceID, env.ID, itm.CollectionID, itm.ID), itm)
-		for _, al := range env.Aliases {
-			m.cache.Set(makeKey(itm.SpaceID, al, itm.CollectionID, itm.ID), itm)
+
+		// Сохраняем в кэш запись без Translations, поскольку значение из кэша также
+		// возвращается только если переводы не запрашиваются
+		itmCached := *itm
+		itmCached.Translations = nil
+		value[localeID] = &itmCached
+		for _, envID := range append(env.Aliases, env.ID) {
+			_ = m.cache.Set(makeKey(itm.SpaceID, envID, itm.CollectionID, itm.ID), value)
 		}
+		return itm.Clone(), err
 	}
-	return itm, err
+
+	return nil, err
 }
 
-func (m cachingMiddleware) Update(ctx context.Context, item *service.Item, options ...*service.UpdateOptions) (err error) {
+func (m cachingMiddleware) invalidateCache(ctx context.Context, item *service.Item) (err error) {
+	env, err := m.envs.Get(ctx, item.SpaceID, item.EnvID)
+	if err != nil {
+		return errors.Wrap(err, "get environment")
+	}
+	for _, a := range append(env.Aliases, env.ID) {
+		key := makeKey(item.SpaceID, a, item.CollectionID, item.ID)
+		_ = m.cache.Remove(key)
+		_ = m.cachePublished.Remove(key)
+	}
+	return nil
+}
 
+// Update вызывает удаление всех сохраненных в кэше значений для записи:
+//   - Каждую из локализаций записи
+//   - С ключами, составленных из разных алиасов окружения
+func (m cachingMiddleware) Update(ctx context.Context, item *service.Item, options ...*service.UpdateOptions) (err error) {
 	err = m.Items.Update(ctx, item, options...)
-	if err == nil {
-		env, err := m.envs.Get(ctx, item.SpaceID, item.EnvID)
-		if err != nil {
-			return err
-		}
-		m.cache.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		m.cachePublished.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-			m.cachePublished.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-		}
+	if err != nil {
+		return err
 	}
-	return err
+	return m.invalidateCache(ctx, item)
 }
 
 func (m cachingMiddleware) Delete(ctx context.Context, del *service.Item, options ...*service.DeleteOptions) (err error) {
-
 	err = m.Items.Delete(ctx, del, options...)
-	if err == nil {
-		env, err := m.envs.Get(ctx, del.SpaceID, del.EnvID)
-		if err != nil {
-			return err
-		}
-		m.cache.Remove(makeKey(del.SpaceID, env.ID, del.CollectionID, del.ID))
-		m.cachePublished.Remove(makeKey(del.SpaceID, env.ID, del.CollectionID, del.ID))
-		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(del.SpaceID, al, del.CollectionID, del.ID))
-			m.cachePublished.Remove(makeKey(del.SpaceID, al, del.CollectionID, del.ID))
-		}
-
+	if err != nil {
+		return err
 	}
-	return err
+	return m.invalidateCache(ctx, del)
 }
 
 func (m cachingMiddleware) Publish(ctx context.Context, item *service.Item, options ...*service.PublishOptions) (err error) {
-
 	err = m.Items.Publish(ctx, item, options...)
-	if err == nil {
-		env, err := m.envs.Get(ctx, item.SpaceID, item.EnvID)
-		if err != nil {
-			return err
-		}
-		m.cache.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		m.cachePublished.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-			m.cachePublished.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-		}
+	if err != nil {
+		return err
 	}
-	return err
+	return m.invalidateCache(ctx, item)
 }
 
 func (m cachingMiddleware) Unpublish(ctx context.Context, item *service.Item, options ...*service.UnpublishOptions) (err error) {
-
 	err = m.Items.Unpublish(ctx, item, options...)
-	if err == nil {
-		env, err := m.envs.Get(ctx, item.SpaceID, item.EnvID)
-		if err != nil {
-			return err
-		}
-		m.cache.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		m.cachePublished.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-			m.cachePublished.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-		}
+	if err != nil {
+		return err
 	}
-	return err
+	return m.invalidateCache(ctx, item)
 }
 
 func (m cachingMiddleware) GetPublished(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*service.GetPublishedOptions) (itm *service.Item, err error) {
-
 	opts := service.MergeGetPublishedOptions(options...)
+	localeID := opts.LocaleID
+	if localeID == "" {
+		localeID = locales.DefaultID
+	}
 
-	val, e := m.cachePublished.Get(makeKey(spaceId, envId, collectionId, itemId))
-	if e == nil {
-		value := val.(map[string]*service.Item)
-		if i, ok := value[opts.LocaleID]; ok {
-			return i, nil
+	// Значение из кэша можно достать только в случае, когда не запрашиваются переводы. Для
+	// списка переводов (`item.Translations`) кэш не предусмотрен: предполагается, что это
+	// нечастый запрос и содержание кэша с разными переводами себя не оправдывает
+	var value = make(map[string]*service.Item)
+	if len(opts.TranslationsIDs) == 0 {
+		val, e := m.cachePublished.Get(makeKey(spaceId, envId, collectionId, itemId))
+		if e == nil {
+			value = val.(map[string]*service.Item)
+			if i, ok := value[localeID]; ok {
+				return i.Clone(), nil
+			}
 		}
 	}
 
 	itm, err = m.Items.GetPublished(ctx, spaceId, envId, collectionId, itemId, opts)
-
 	if err == nil {
 		env, err := m.envs.Get(ctx, itm.SpaceID, itm.EnvID)
 		if err != nil {
 			return nil, err
 		}
-		var value = make(map[string]*service.Item)
-		if val != nil {
-			value = val.(map[string]*service.Item)
-		}
-		value[opts.LocaleID] = itm
-		m.cachePublished.Set(makeKey(itm.SpaceID, env.ID, itm.CollectionID, itm.ID), value)
-		for _, al := range env.Aliases {
-			m.cachePublished.Set(makeKey(itm.SpaceID, al, itm.CollectionID, itm.ID), value)
+
+		// Сохраняем в кэш запись без Translations, поскольку значение из кэша также
+		// возвращается только если переводы не запрашиваются
+		itmCached := *itm
+		itmCached.Translations = nil
+		value[localeID] = &itmCached
+		for _, envID := range append(env.Aliases, env.ID) {
+			_ = m.cachePublished.Set(makeKey(itm.SpaceID, envID, itm.CollectionID, itm.ID), value)
 		}
+		return itm.Clone(), err
 	}
 
-	return itm, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Archive(ctx context.Context, item *service.Item, options ...*service.ArchiveOptions) (err error) {
-
 	err = m.Items.Archive(ctx, item, options...)
-	if err == nil {
-		env, err := m.envs.Get(ctx, item.SpaceID, item.EnvID)
-		if err != nil {
-			return err
-		}
-		m.cache.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		m.cachePublished.Remove(makeKey(item.SpaceID, env.ID, item.CollectionID, item.ID))
-		for _, al := range env.Aliases {
-			m.cache.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-			m.cachePublished.Remove(makeKey(item.SpaceID, al, item.CollectionID, item.ID))
-		}
+	if err != nil {
+		return err
 	}
-	return err
+	return m.invalidateCache(ctx, item)
 }
diff --git a/pkg/items/middleware/caching_middleware_test.go b/pkg/items/middleware/caching_middleware_test.go
index c3e862b1ffe3dd5e7fdde976cc4aa09e316a600f..4d777647788f290acf4e55550c435fa7ba27153a 100644
--- a/pkg/items/middleware/caching_middleware_test.go
+++ b/pkg/items/middleware/caching_middleware_test.go
@@ -47,10 +47,10 @@ func TestItemsCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кеша, при повторном запросе.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша, при повторном запросе.")
 
 		v3, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
-		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+		assert.Equal(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
 		require.NoError(t, err)
 
 		env.AssertExpectations(t)
@@ -71,10 +71,11 @@ func TestItemsCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кеша, при повторном запросе.")
+		assert.Equal(t, v1, v2, "Ожидается получение равного объекта из кеша при повторном запросе.")
+		assert.NotSame(t, v1, v2, "Создается копия объекта для хранения в кэше")
 
 		v3, err := svc.Get(ctx, spaceID, envID, colID, itemID)
-		assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+		assert.Equal(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
 		require.NoError(t, err)
 
 		env.AssertExpectations(t)
@@ -95,11 +96,11 @@ func TestItemsCache(t *testing.T) {
 
 		v2, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 		v3, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 		require.NoError(t, err)
-		assert.Same(t, v2, v3, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
+		assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
 
 		env.AssertExpectations(t)
 		itms.AssertExpectations(t)
@@ -119,11 +120,11 @@ func TestItemsCache(t *testing.T) {
 
 		v2, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 		v3, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 		require.NoError(t, err)
-		assert.Same(t, v2, v3, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
+		assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
 
 		env.AssertExpectations(t)
 		itms.AssertExpectations(t)
@@ -152,19 +153,19 @@ func TestItemsCache(t *testing.T) {
 
 		v2loc1, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: loc1})
 		require.NoError(t, err)
-		assert.Same(t, v1loc1, v2loc1, "Ожидается получение объекта c локализацией loc1 из кеша.")
+		assert.Equal(t, v1loc1, v2loc1, "Ожидается получение объекта c локализацией loc1 из кеша.")
 
 		v2loc2, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: loc2})
 		require.NoError(t, err)
-		assert.Same(t, v1loc2, v2loc2, "Ожидается получение объекта c локализацией loc2 из кеша.")
+		assert.Equal(t, v1loc2, v2loc2, "Ожидается получение объекта c локализацией loc2 из кеша.")
 
 		v3loc1, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: loc1})
 		require.NoError(t, err)
-		assert.Same(t, v2loc1, v3loc1, "Ожидается получение объекта c локализацией loc1 из кеша, при запросе того же объекта по ID окружения.")
+		assert.Equal(t, v2loc1, v3loc1, "Ожидается получение объекта c локализацией loc1 из кеша, при запросе того же объекта по ID окружения.")
 
 		v3loc2, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: loc2})
 		require.NoError(t, err)
-		assert.Same(t, v2loc2, v3loc2, "Ожидается получение объекта c локализацией loc2 из кеша, при запросе того же объекта по ID окружения.")
+		assert.Equal(t, v2loc2, v3loc2, "Ожидается получение объекта c локализацией loc2 из кеша, при запросе того же объекта по ID окружения.")
 
 		env.AssertExpectations(t)
 		itms.AssertExpectations(t)
@@ -185,7 +186,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			itms.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -198,7 +199,7 @@ func TestItemsCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кэша после обновления и получение его заново из сервиса.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кэша после обновления и получение его заново из сервиса.")
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
@@ -218,7 +219,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			itms.On("Archive", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -231,7 +232,7 @@ func TestItemsCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кэша после архивации и получение из сервиса.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кэша после архивации и получение из сервиса.")
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
@@ -251,7 +252,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			itms.On("Publish", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -263,7 +264,7 @@ func TestItemsCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кэша после публикации и получение заново из сервиса.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кэша после публикации и получение заново из сервиса.")
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
@@ -283,11 +284,11 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе.")
 
 			v3, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по alias окружения.")
 
 			itms.On("Delete", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -323,7 +324,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
 			itms.On("Unpublish", mock.Anything, mock.Anything).Return(nil).Once()
@@ -336,7 +337,7 @@ func TestItemsCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кэша после снятия с публикации и получение заново из сервиса.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кэша после снятия с публикации и получение заново из сервиса.")
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
@@ -356,7 +357,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по alias окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по alias окружения.")
 
 			itms.On("Publish", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -389,7 +390,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по alias окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по alias окружения.")
 
 			itms.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -402,7 +403,7 @@ func TestItemsCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кэша при обновлении и получение из сервиса по alias окружения.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кэша при обновлении и получение из сервиса по alias окружения.")
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
@@ -422,7 +423,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по alias окружения.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша по alias окружения.")
 
 			itms.On("Unpublish", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -435,7 +436,7 @@ func TestItemsCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, envAlias, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кэша после снятия с публикации и получение из сервиса по alias окружения.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кэша после снятия с публикации и получение из сервиса по alias окружения.")
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
@@ -455,11 +456,11 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			v3, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
 
 			itms.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -496,11 +497,11 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			v3, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
 
 			itms.On("Archive", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -537,11 +538,11 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			v3, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
 
 			itms.On("Delete", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -578,11 +579,11 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			v3, err := svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
+			assert.Equal(t, v2, v3, "Ожидается получение объекта из кеша по о alias окружения.")
 
 			itms.On("Unpublish", mock.Anything, mock.Anything).Return(nil).Once()
 			env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
@@ -623,14 +624,14 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			v3, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
 
 			v4, err := svc.GetPublished(ctx, spaceID, envID, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.NoError(t, err)
-			assert.Same(t, v3, v4, "Ожидается получение опубликованного объекта из кеша.")
+			assert.Equal(t, v3, v4, "Ожидается получение опубликованного объекта из кеша.")
 
 			itms.On("Unpublish", mock.Anything, mock.Anything).Return(nil).Once()
 			err = svc.Unpublish(ctx, &items.Item{ID: itemID, SpaceID: spaceID, EnvID: envAlias, CollectionID: colID, State: items.StatePublished})
@@ -643,7 +644,7 @@ func TestItemsCache(t *testing.T) {
 
 			v5, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.NotSame(t, v5, v2, "Ожидается удаление объекта из кэша и получение заново из сервиса.")
+			assert.NotEqual(t, v5, v2, "Ожидается удаление объекта из кэша и получение заново из сервиса.")
 
 			_, err = svc.GetPublished(ctx, spaceID, envAlias, colID, itemID, &items.GetPublishedOptions{LocaleID: locID})
 			require.Error(t, err)
@@ -667,7 +668,7 @@ func TestItemsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кеша.")
 
 			time.Sleep(2 * ttl)
 
@@ -677,6 +678,7 @@ func TestItemsCache(t *testing.T) {
 			v3, err := svc.Get(ctx, spaceID, envID, colID, itemID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается удаление объекта из кэша и получение из сервиса.")
+			assert.Equal(t, v2, v3)
 
 			env.AssertExpectations(t)
 			itms.AssertExpectations(t)
diff --git a/pkg/items/middleware/logging_middleware.go b/pkg/items/middleware/logging_middleware.go
index c8271c58dd1e0c09862bcad7b7c8f60817346a40..1178719ee2ad0190e12316165b2536de67f1324e 100644
--- a/pkg/items/middleware/logging_middleware.go
+++ b/pkg/items/middleware/logging_middleware.go
@@ -26,7 +26,7 @@ func LoggingMiddleware(logger *zap.Logger) Middleware {
 
 func (m *loggingMiddleware) Aggregate(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregateOptions) (result map[string]interface{}, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	result, err = m.next.Aggregate(ctx, spaceId, envId, collectionId, filter, options...)
@@ -40,7 +40,7 @@ func (m *loggingMiddleware) Aggregate(ctx context.Context, spaceId string, envId
 
 func (m *loggingMiddleware) AggregatePublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.AggregatePublishedOptions) (result map[string]interface{}, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	result, err = m.next.AggregatePublished(ctx, spaceId, envId, collectionId, filter, options...)
@@ -58,7 +58,7 @@ func (m *loggingMiddleware) Archive(ctx context.Context, item *items.Item, optio
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventArchive),
 		logzap.Object(item),
 	)
@@ -79,7 +79,7 @@ func (m *loggingMiddleware) Create(ctx context.Context, item *items.Item, opts .
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventCreate),
 	)
 
@@ -99,7 +99,7 @@ func (m *loggingMiddleware) Delete(ctx context.Context, item *items.Item, option
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventDelete),
 		logzap.Object(item),
 	)
@@ -116,7 +116,7 @@ func (m *loggingMiddleware) Delete(ctx context.Context, item *items.Item, option
 
 func (m *loggingMiddleware) Find(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindOptions) (items []*items.Item, total int, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	items, total, err = m.next.Find(ctx, spaceId, envId, collectionId, filter, options...)
@@ -131,7 +131,7 @@ func (m *loggingMiddleware) Find(ctx context.Context, spaceId string, envId stri
 
 func (m *loggingMiddleware) FindArchived(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindArchivedOptions) (items []*items.Item, total int, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	items, total, err = m.next.FindArchived(ctx, spaceId, envId, collectionId, filter, options...)
@@ -145,7 +145,7 @@ func (m *loggingMiddleware) FindArchived(ctx context.Context, spaceId string, en
 
 func (m *loggingMiddleware) FindPublished(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options ...*items.FindPublishedOptions) (items []*items.Item, total int, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 	)
 
 	items, total, err = m.next.FindPublished(ctx, spaceId, envId, collectionId, filter, options...)
@@ -159,7 +159,7 @@ func (m *loggingMiddleware) FindPublished(ctx context.Context, spaceId string, e
 
 func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetOptions) (item *items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -174,7 +174,7 @@ func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId strin
 
 func (m *loggingMiddleware) GetPublished(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.GetPublishedOptions) (item *items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -189,7 +189,7 @@ func (m *loggingMiddleware) GetPublished(ctx context.Context, spaceId string, en
 
 func (m *loggingMiddleware) GetRevision(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, revisionId string, options ...*items.GetRevisionOptions) (item *items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -208,7 +208,7 @@ func (m *loggingMiddleware) Introspect(ctx context.Context, item *items.Item, op
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Object(item),
 	)
 
@@ -223,7 +223,7 @@ func (m *loggingMiddleware) Introspect(ctx context.Context, item *items.Item, op
 
 func (m *loggingMiddleware) ListRevisions(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, options ...*items.ListRevisionsOptions) (items []*items.Item, err error) {
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceId),
+		logzap.Caller(ctx, logzap.WithSpace(spaceId)),
 		logzap.Object(id.NewItemId(spaceId, envId, collectionId, itemId)),
 	)
 
@@ -242,7 +242,7 @@ func (m *loggingMiddleware) Publish(ctx context.Context, item *items.Item, optio
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventPublish),
 		logzap.Object(item),
 	)
@@ -263,7 +263,7 @@ func (m *loggingMiddleware) Unarchive(ctx context.Context, item *items.Item, opt
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUnarchive),
 		logzap.Object(item),
 	)
@@ -284,7 +284,7 @@ func (m *loggingMiddleware) Undelete(ctx context.Context, item *items.Item, opti
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUndelete),
 		logzap.Object(item),
 	)
@@ -305,7 +305,7 @@ func (m *loggingMiddleware) Unpublish(ctx context.Context, item *items.Item, opt
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUnpublish),
 		logzap.Object(item),
 	)
@@ -326,7 +326,7 @@ func (m *loggingMiddleware) Update(ctx context.Context, item *items.Item, option
 		spaceID = item.SpaceID
 	}
 	logger := m.logger.With(
-		logzap.CallerFromContext(ctx, spaceID),
+		logzap.Caller(ctx, logzap.WithSpace(spaceID)),
 		logzap.Event(items.EventUpdate),
 		logzap.Object(item),
 	)
diff --git a/pkg/items/mocks/Decoder.go b/pkg/items/mocks/Decoder.go
index b1dbd9aa526cf4fe4a62d29f652c3e2a513924b3..fcc4d502ee3d57504560a69c35ebdea06460b595 100644
--- a/pkg/items/mocks/Decoder.go
+++ b/pkg/items/mocks/Decoder.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -16,6 +16,10 @@ type Decoder struct {
 func (_m *Decoder) Decode(value interface{}, item *items.Item) error {
 	ret := _m.Called(value, item)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Decode")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(interface{}, *items.Item) error); ok {
 		r0 = rf(value, item)
diff --git a/pkg/items/mocks/Encoder.go b/pkg/items/mocks/Encoder.go
index c19203b2448930eb75e62195837a1b22cd9cfe42..cd2dd5c9762c84131974b6e612086ad61d3699f3 100644
--- a/pkg/items/mocks/Encoder.go
+++ b/pkg/items/mocks/Encoder.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -16,6 +16,10 @@ type Encoder struct {
 func (_m *Encoder) Encode(item *items.Item) (interface{}, error) {
 	ret := _m.Called(item)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Encode")
+	}
+
 	var r0 interface{}
 	var r1 error
 	if rf, ok := ret.Get(0).(func(*items.Item) (interface{}, error)); ok {
diff --git a/pkg/items/mocks/ItemObserver.go b/pkg/items/mocks/ItemObserver.go
index d826e04045f01e504200e742afa16e68181bc6c9..e3451e680e643ac49343830a4713b93d6ad174c6 100644
--- a/pkg/items/mocks/ItemObserver.go
+++ b/pkg/items/mocks/ItemObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
diff --git a/pkg/items/mocks/ItemReadObserver.go b/pkg/items/mocks/ItemReadObserver.go
index 212f09ad7a046086f1cc99230699a304ec2a9878..c6876a5a3aada6942bf07fdfdfcee35f626353c9 100644
--- a/pkg/items/mocks/ItemReadObserver.go
+++ b/pkg/items/mocks/ItemReadObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -18,6 +18,10 @@ type ItemReadObserver struct {
 func (_m *ItemReadObserver) OnPostFind(ctx context.Context, _a1 []*items.Item, total int) ([]*items.Item, int, error) {
 	ret := _m.Called(ctx, _a1, total)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnPostFind")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
@@ -51,6 +55,10 @@ func (_m *ItemReadObserver) OnPostFind(ctx context.Context, _a1 []*items.Item, t
 func (_m *ItemReadObserver) OnPostGet(ctx context.Context, item *items.Item) (*items.Item, error) {
 	ret := _m.Called(ctx, item)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnPostGet")
+	}
+
 	var r0 *items.Item
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item) (*items.Item, error)); ok {
@@ -77,6 +85,10 @@ func (_m *ItemReadObserver) OnPostGet(ctx context.Context, item *items.Item) (*i
 func (_m *ItemReadObserver) OnPreFind(ctx context.Context, spaceId string, envId string, collectionId string, filter *items.Filter, options *items.FindOptions) error {
 	ret := _m.Called(ctx, spaceId, envId, collectionId, filter, options)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnPreFind")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *items.Filter, *items.FindOptions) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, filter, options)
@@ -91,6 +103,10 @@ func (_m *ItemReadObserver) OnPreFind(ctx context.Context, spaceId string, envId
 func (_m *ItemReadObserver) OnPreGet(ctx context.Context, spaceId string, envId string, collectionId string, itemId string) error {
 	ret := _m.Called(ctx, spaceId, envId, collectionId, itemId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnPreGet")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId)
diff --git a/pkg/items/mocks/Items.go b/pkg/items/mocks/Items.go
index 9bbd8948668a7ec665fdefd2f5e086cb235d579d..e134822e2869693fa3801affac8660eb357cd70c 100644
--- a/pkg/items/mocks/Items.go
+++ b/pkg/items/mocks/Items.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -27,6 +27,10 @@ func (_m *Items) Aggregate(ctx context.Context, spaceId string, envId string, co
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Aggregate")
+	}
+
 	var r0 map[string]interface{}
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *items.Filter, ...*items.AggregateOptions) (map[string]interface{}, error)); ok {
@@ -60,6 +64,10 @@ func (_m *Items) AggregatePublished(ctx context.Context, spaceId string, envId s
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for AggregatePublished")
+	}
+
 	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 {
@@ -93,6 +101,10 @@ func (_m *Items) Archive(ctx context.Context, item *items.Item, options ...*item
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Archive")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.ArchiveOptions) error); ok {
 		r0 = rf(ctx, item, options...)
@@ -114,6 +126,10 @@ func (_m *Items) Create(ctx context.Context, item *items.Item, opts ...*items.Cr
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Create")
+	}
+
 	var r0 *items.Item
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.CreateOptions) (*items.Item, error)); ok {
@@ -147,6 +163,10 @@ func (_m *Items) Delete(ctx context.Context, item *items.Item, options ...*items
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Delete")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.DeleteOptions) error); ok {
 		r0 = rf(ctx, item, options...)
@@ -168,6 +188,10 @@ func (_m *Items) Find(ctx context.Context, spaceId string, envId string, collect
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Find")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
@@ -208,6 +232,10 @@ func (_m *Items) FindArchived(ctx context.Context, spaceId string, envId string,
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for FindArchived")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
@@ -248,6 +276,10 @@ func (_m *Items) FindPublished(ctx context.Context, spaceId string, envId string
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for FindPublished")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
@@ -288,6 +320,10 @@ func (_m *Items) Get(ctx context.Context, spaceId string, envId string, collecti
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Get")
+	}
+
 	var r0 *items.Item
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.GetOptions) (*items.Item, error)); ok {
@@ -321,6 +357,10 @@ func (_m *Items) GetPublished(ctx context.Context, spaceId string, envId string,
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for GetPublished")
+	}
+
 	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 {
@@ -354,6 +394,10 @@ func (_m *Items) GetRevision(ctx context.Context, spaceId string, envId string,
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for GetRevision")
+	}
+
 	var r0 *items.Item
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string, ...*items.GetRevisionOptions) (*items.Item, error)); ok {
@@ -387,6 +431,10 @@ func (_m *Items) Introspect(ctx context.Context, item *items.Item, opts ...*item
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Introspect")
+	}
+
 	var r0 *items.Item
 	var r1 *schema.Schema
 	var r2 error
@@ -429,6 +477,10 @@ func (_m *Items) ListRevisions(ctx context.Context, spaceId string, envId string
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for ListRevisions")
+	}
+
 	var r0 []*items.Item
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.ListRevisionsOptions) ([]*items.Item, error)); ok {
@@ -462,6 +514,10 @@ func (_m *Items) Publish(ctx context.Context, item *items.Item, options ...*item
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Publish")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.PublishOptions) error); ok {
 		r0 = rf(ctx, item, options...)
@@ -483,6 +539,10 @@ func (_m *Items) Unarchive(ctx context.Context, item *items.Item, options ...*it
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Unarchive")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.UnarchiveOptions) error); ok {
 		r0 = rf(ctx, item, options...)
@@ -504,6 +564,10 @@ func (_m *Items) Undelete(ctx context.Context, item *items.Item, options ...*ite
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Undelete")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.UndeleteOptions) error); ok {
 		r0 = rf(ctx, item, options...)
@@ -525,6 +589,10 @@ func (_m *Items) Unpublish(ctx context.Context, item *items.Item, options ...*it
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Unpublish")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.UnpublishOptions) error); ok {
 		r0 = rf(ctx, item, options...)
@@ -546,6 +614,10 @@ func (_m *Items) Update(ctx context.Context, item *items.Item, options ...*items
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Update")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.UpdateOptions) error); ok {
 		r0 = rf(ctx, item, options...)
diff --git a/pkg/items/mocks/Middleware.go b/pkg/items/mocks/Middleware.go
index 1cde36c075701f58cd108a677fc73537cf25bf52..0bebef848868bd06d6539b5159f5973f6a39315e 100644
--- a/pkg/items/mocks/Middleware.go
+++ b/pkg/items/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -17,6 +17,10 @@ type Middleware struct {
 func (_m *Middleware) Execute(_a0 items.Items) items.Items {
 	ret := _m.Called(_a0)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Execute")
+	}
+
 	var r0 items.Items
 	if rf, ok := ret.Get(0).(func(items.Items) items.Items); ok {
 		r0 = rf(_a0)
diff --git a/pkg/items/mocks/PreSaver.go b/pkg/items/mocks/PreSaver.go
index 729c1ebfc4e7e60e2920b082e22f35617bf22e4e..3acc32732fcea3df459a5234fb5c952e823edfaa 100644
--- a/pkg/items/mocks/PreSaver.go
+++ b/pkg/items/mocks/PreSaver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -20,6 +20,10 @@ type PreSaver struct {
 func (_m *PreSaver) PreSave(ctx context.Context, f *field.Field, v interface{}, itemCtx *items.Context) (interface{}, bool, error) {
 	ret := _m.Called(ctx, f, v, itemCtx)
 
+	if len(ret) == 0 {
+		panic("no return value specified for PreSave")
+	}
+
 	var r0 interface{}
 	var r1 bool
 	var r2 error
diff --git a/pkg/items/mocks/ProcessDataFunc.go b/pkg/items/mocks/ProcessDataFunc.go
index 6ba38c984c7bba69461acc206393ebb66ba1cc07..7689da0df40591ba68ca8a6eb3a160d475a81c90 100644
--- a/pkg/items/mocks/ProcessDataFunc.go
+++ b/pkg/items/mocks/ProcessDataFunc.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -19,6 +19,10 @@ type ProcessDataFunc struct {
 func (_m *ProcessDataFunc) Execute(ctx context.Context, sch *schema.Schema, data map[string]interface{}) (map[string]interface{}, error) {
 	ret := _m.Called(ctx, sch, data)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Execute")
+	}
+
 	var r0 map[string]interface{}
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *schema.Schema, map[string]interface{}) (map[string]interface{}, error)); ok {
diff --git a/pkg/items/mocks/Storage.go b/pkg/items/mocks/Storage.go
index 6f8082b98690dab97058c6775f3a53c2c655c80f..4ce04d02d6985f5e4da86137d74763999da02136 100644
--- a/pkg/items/mocks/Storage.go
+++ b/pkg/items/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.33.3. DO NOT EDIT.
+// Code generated by mockery v2.45.0. DO NOT EDIT.
 
 package mocks
 
@@ -9,6 +9,8 @@ import (
 
 	items "git.perx.ru/perxis/perxis-go/pkg/items"
 
+	locales "git.perx.ru/perxis/perxis-go/pkg/locales"
+
 	mock "github.com/stretchr/testify/mock"
 )
 
@@ -28,6 +30,10 @@ func (_m *Storage) Aggregate(ctx context.Context, coll *collections.Collection,
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Aggregate")
+	}
+
 	var r0 map[string]interface{}
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.AggregateOptions) (map[string]interface{}, error)); ok {
@@ -61,6 +67,10 @@ func (_m *Storage) AggregatePublished(ctx context.Context, coll *collections.Col
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for AggregatePublished")
+	}
+
 	var r0 map[string]interface{}
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.AggregatePublishedOptions) (map[string]interface{}, error)); ok {
@@ -94,6 +104,10 @@ func (_m *Storage) Archive(ctx context.Context, archived *items.Item, options ..
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Archive")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.ArchiveOptions) error); ok {
 		r0 = rf(ctx, archived, options...)
@@ -108,6 +122,10 @@ func (_m *Storage) Archive(ctx context.Context, archived *items.Item, options ..
 func (_m *Storage) ChangeRevisionsItemID(ctx context.Context, spaceId string, envId string, collectionId string, itemId string, newItemId string) error {
 	ret := _m.Called(ctx, spaceId, envId, collectionId, itemId, newItemId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for ChangeRevisionsItemID")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, string) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId, newItemId)
@@ -118,6 +136,24 @@ func (_m *Storage) ChangeRevisionsItemID(ctx context.Context, spaceId string, en
 	return r0
 }
 
+// CleanUpRevisions provides a mock function with given fields: ctx, coll, itemId
+func (_m *Storage) CleanUpRevisions(ctx context.Context, coll *collections.Collection, itemId string) error {
+	ret := _m.Called(ctx, coll, itemId)
+
+	if len(ret) == 0 {
+		panic("no return value specified for CleanUpRevisions")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string) error); ok {
+		r0 = rf(ctx, coll, itemId)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 // Copy provides a mock function with given fields: ctx, src, dst, itemSets
 func (_m *Storage) Copy(ctx context.Context, src *collections.Collection, dst *collections.Collection, itemSets ...string) error {
 	_va := make([]interface{}, len(itemSets))
@@ -129,6 +165,10 @@ func (_m *Storage) Copy(ctx context.Context, src *collections.Collection, dst *c
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Copy")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection, ...string) error); ok {
 		r0 = rf(ctx, src, dst, itemSets...)
@@ -150,6 +190,10 @@ func (_m *Storage) Create(ctx context.Context, coll *collections.Collection, ite
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Create")
+	}
+
 	var r0 *items.Item
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Item, ...*items.CreateOptions) (*items.Item, error)); ok {
@@ -176,6 +220,10 @@ func (_m *Storage) Create(ctx context.Context, coll *collections.Collection, ite
 func (_m *Storage) CreateRevision(ctx context.Context, spaceId string, envId string, collectionId string, itemId string) error {
 	ret := _m.Called(ctx, spaceId, envId, collectionId, itemId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for CreateRevision")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId)
@@ -187,7 +235,7 @@ func (_m *Storage) CreateRevision(ctx context.Context, spaceId string, envId str
 }
 
 // Find provides a mock function with given fields: ctx, coll, filter, opts
-func (_m *Storage) Find(ctx context.Context, coll *collections.Collection, filter *items.Filter, opts ...*items.FindOptions) ([]*items.Item, int, error) {
+func (_m *Storage) Find(ctx context.Context, coll *collections.Collection, filter *items.Filter, opts ...*items.StorageFindOptions) ([]*items.Item, int, error) {
 	_va := make([]interface{}, len(opts))
 	for _i := range opts {
 		_va[_i] = opts[_i]
@@ -197,13 +245,17 @@ func (_m *Storage) Find(ctx context.Context, coll *collections.Collection, filte
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Find")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindOptions) ([]*items.Item, int, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) ([]*items.Item, int, error)); ok {
 		return rf(ctx, coll, filter, opts...)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindOptions) []*items.Item); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) []*items.Item); ok {
 		r0 = rf(ctx, coll, filter, opts...)
 	} else {
 		if ret.Get(0) != nil {
@@ -211,13 +263,13 @@ func (_m *Storage) Find(ctx context.Context, coll *collections.Collection, filte
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindOptions) int); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) int); ok {
 		r1 = rf(ctx, coll, filter, opts...)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindOptions) error); ok {
+	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) error); ok {
 		r2 = rf(ctx, coll, filter, opts...)
 	} else {
 		r2 = ret.Error(2)
@@ -227,7 +279,7 @@ func (_m *Storage) Find(ctx context.Context, coll *collections.Collection, filte
 }
 
 // FindArchived provides a mock function with given fields: ctx, coll, filter, opts
-func (_m *Storage) FindArchived(ctx context.Context, coll *collections.Collection, filter *items.Filter, opts ...*items.FindArchivedOptions) ([]*items.Item, int, error) {
+func (_m *Storage) FindArchived(ctx context.Context, coll *collections.Collection, filter *items.Filter, opts ...*items.StorageFindOptions) ([]*items.Item, int, error) {
 	_va := make([]interface{}, len(opts))
 	for _i := range opts {
 		_va[_i] = opts[_i]
@@ -237,13 +289,17 @@ func (_m *Storage) FindArchived(ctx context.Context, coll *collections.Collectio
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for FindArchived")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindArchivedOptions) ([]*items.Item, int, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) ([]*items.Item, int, error)); ok {
 		return rf(ctx, coll, filter, opts...)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindArchivedOptions) []*items.Item); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) []*items.Item); ok {
 		r0 = rf(ctx, coll, filter, opts...)
 	} else {
 		if ret.Get(0) != nil {
@@ -251,13 +307,13 @@ func (_m *Storage) FindArchived(ctx context.Context, coll *collections.Collectio
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindArchivedOptions) int); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) int); ok {
 		r1 = rf(ctx, coll, filter, opts...)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindArchivedOptions) error); ok {
+	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) error); ok {
 		r2 = rf(ctx, coll, filter, opts...)
 	} else {
 		r2 = ret.Error(2)
@@ -267,7 +323,7 @@ func (_m *Storage) FindArchived(ctx context.Context, coll *collections.Collectio
 }
 
 // FindPublished provides a mock function with given fields: ctx, coll, filter, opts
-func (_m *Storage) FindPublished(ctx context.Context, coll *collections.Collection, filter *items.Filter, opts ...*items.FindPublishedOptions) ([]*items.Item, int, error) {
+func (_m *Storage) FindPublished(ctx context.Context, coll *collections.Collection, filter *items.Filter, opts ...*items.StorageFindOptions) ([]*items.Item, int, error) {
 	_va := make([]interface{}, len(opts))
 	for _i := range opts {
 		_va[_i] = opts[_i]
@@ -277,13 +333,17 @@ func (_m *Storage) FindPublished(ctx context.Context, coll *collections.Collecti
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for FindPublished")
+	}
+
 	var r0 []*items.Item
 	var r1 int
 	var r2 error
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindPublishedOptions) ([]*items.Item, int, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) ([]*items.Item, int, error)); ok {
 		return rf(ctx, coll, filter, opts...)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindPublishedOptions) []*items.Item); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) []*items.Item); ok {
 		r0 = rf(ctx, coll, filter, opts...)
 	} else {
 		if ret.Get(0) != nil {
@@ -291,13 +351,13 @@ func (_m *Storage) FindPublished(ctx context.Context, coll *collections.Collecti
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindPublishedOptions) int); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) int); ok {
 		r1 = rf(ctx, coll, filter, opts...)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection, *items.Filter, ...*items.FindPublishedOptions) error); ok {
+	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection, *items.Filter, ...*items.StorageFindOptions) error); ok {
 		r2 = rf(ctx, coll, filter, opts...)
 	} else {
 		r2 = ret.Error(2)
@@ -307,7 +367,7 @@ func (_m *Storage) FindPublished(ctx context.Context, coll *collections.Collecti
 }
 
 // GetRevision provides a mock function with given fields: ctx, coll, itemId, revisionId, options
-func (_m *Storage) GetRevision(ctx context.Context, coll *collections.Collection, itemId string, revisionId string, options ...*items.GetRevisionOptions) (*items.Item, error) {
+func (_m *Storage) GetRevision(ctx context.Context, coll *collections.Collection, itemId string, revisionId string, options ...*items.StorageFindOptions) (*items.Item, error) {
 	_va := make([]interface{}, len(options))
 	for _i := range options {
 		_va[_i] = options[_i]
@@ -317,12 +377,16 @@ func (_m *Storage) GetRevision(ctx context.Context, coll *collections.Collection
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for GetRevision")
+	}
+
 	var r0 *items.Item
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, string, ...*items.GetRevisionOptions) (*items.Item, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, string, ...*items.StorageFindOptions) (*items.Item, error)); ok {
 		return rf(ctx, coll, itemId, revisionId, options...)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, string, ...*items.GetRevisionOptions) *items.Item); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, string, ...*items.StorageFindOptions) *items.Item); ok {
 		r0 = rf(ctx, coll, itemId, revisionId, options...)
 	} else {
 		if ret.Get(0) != nil {
@@ -330,7 +394,7 @@ func (_m *Storage) GetRevision(ctx context.Context, coll *collections.Collection
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, string, string, ...*items.GetRevisionOptions) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, string, string, ...*items.StorageFindOptions) error); ok {
 		r1 = rf(ctx, coll, itemId, revisionId, options...)
 	} else {
 		r1 = ret.Error(1)
@@ -339,13 +403,24 @@ func (_m *Storage) GetRevision(ctx context.Context, coll *collections.Collection
 	return r0, r1
 }
 
-// Init provides a mock function with given fields: ctx, collection
-func (_m *Storage) Init(ctx context.Context, collection *collections.Collection) error {
-	ret := _m.Called(ctx, collection)
+// Init provides a mock function with given fields: ctx, collection, _a2
+func (_m *Storage) Init(ctx context.Context, collection *collections.Collection, _a2 ...*locales.Locale) error {
+	_va := make([]interface{}, len(_a2))
+	for _i := range _a2 {
+		_va[_i] = _a2[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, collection)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Init")
+	}
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) error); ok {
-		r0 = rf(ctx, collection)
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, ...*locales.Locale) error); ok {
+		r0 = rf(ctx, collection, _a2...)
 	} else {
 		r0 = ret.Error(0)
 	}
@@ -354,7 +429,7 @@ func (_m *Storage) Init(ctx context.Context, collection *collections.Collection)
 }
 
 // ListRevisions provides a mock function with given fields: ctx, coll, itemId, options
-func (_m *Storage) ListRevisions(ctx context.Context, coll *collections.Collection, itemId string, options ...*items.ListRevisionsOptions) ([]*items.Item, error) {
+func (_m *Storage) ListRevisions(ctx context.Context, coll *collections.Collection, itemId string, options ...*items.StorageFindOptions) ([]*items.Item, error) {
 	_va := make([]interface{}, len(options))
 	for _i := range options {
 		_va[_i] = options[_i]
@@ -364,12 +439,16 @@ func (_m *Storage) ListRevisions(ctx context.Context, coll *collections.Collecti
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for ListRevisions")
+	}
+
 	var r0 []*items.Item
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, ...*items.ListRevisionsOptions) ([]*items.Item, error)); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, ...*items.StorageFindOptions) ([]*items.Item, error)); ok {
 		return rf(ctx, coll, itemId, options...)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, ...*items.ListRevisionsOptions) []*items.Item); ok {
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, string, ...*items.StorageFindOptions) []*items.Item); ok {
 		r0 = rf(ctx, coll, itemId, options...)
 	} else {
 		if ret.Get(0) != nil {
@@ -377,7 +456,7 @@ func (_m *Storage) ListRevisions(ctx context.Context, coll *collections.Collecti
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, string, ...*items.ListRevisionsOptions) error); ok {
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, string, ...*items.StorageFindOptions) error); ok {
 		r1 = rf(ctx, coll, itemId, options...)
 	} else {
 		r1 = ret.Error(1)
@@ -397,6 +476,10 @@ func (_m *Storage) Publish(ctx context.Context, published *items.Item, options .
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Publish")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.PublishOptions) error); ok {
 		r0 = rf(ctx, published, options...)
@@ -418,6 +501,10 @@ func (_m *Storage) RemoveArchived(ctx context.Context, spaceId string, envId str
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for RemoveArchived")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.DeleteOptions) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId, options...)
@@ -439,6 +526,10 @@ func (_m *Storage) RemoveItems(ctx context.Context, spaceId string, envId string
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for RemoveItems")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.DeleteOptions) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId, options...)
@@ -460,6 +551,10 @@ func (_m *Storage) RemovePublished(ctx context.Context, spaceId string, envId st
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for RemovePublished")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.DeleteOptions) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId, options...)
@@ -481,6 +576,10 @@ func (_m *Storage) RemoveRevision(ctx context.Context, spaceId string, envId str
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for RemoveRevision")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.DeleteOptions) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, revision, options...)
@@ -502,6 +601,10 @@ func (_m *Storage) RemoveRevisions(ctx context.Context, spaceId string, envId st
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for RemoveRevisions")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.DeleteOptions) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId, options...)
@@ -516,6 +619,10 @@ func (_m *Storage) RemoveRevisions(ctx context.Context, spaceId string, envId st
 func (_m *Storage) Reset(ctx context.Context, spaceId string, envId string, collectionId string) error {
 	ret := _m.Called(ctx, spaceId, envId, collectionId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Reset")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId)
@@ -537,6 +644,10 @@ func (_m *Storage) Unarchive(ctx context.Context, unarchived *items.Item, option
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Unarchive")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.UnarchiveOptions) error); ok {
 		r0 = rf(ctx, unarchived, options...)
@@ -558,6 +669,10 @@ func (_m *Storage) Unpublish(ctx context.Context, unpublished *items.Item, optio
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Unpublish")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *items.Item, ...*items.UnpublishOptions) error); ok {
 		r0 = rf(ctx, unpublished, options...)
@@ -579,6 +694,10 @@ func (_m *Storage) Update(ctx context.Context, coll *collections.Collection, ite
 	_ca = append(_ca, _va...)
 	ret := _m.Called(_ca...)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Update")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *items.Item, ...*items.UpdateOptions) error); ok {
 		r0 = rf(ctx, coll, item, options...)
diff --git a/pkg/items/options.go b/pkg/items/options.go
index 6794ddde79a864ac58188d8fd3cc600cbb18cd10..cdd98918d3e35a09f2d2fe2d9971781013bace1e 100644
--- a/pkg/items/options.go
+++ b/pkg/items/options.go
@@ -1,6 +1,12 @@
 package items
 
-import "git.perx.ru/perxis/perxis-go/pkg/options"
+import (
+	"maps"
+	"slices"
+
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	pb "git.perx.ru/perxis/perxis-go/proto/items"
+)
 
 type Options struct {
 	Env               map[string]interface{}
@@ -50,9 +56,27 @@ func MergeCreateOptions(opts ...*CreateOptions) *CreateOptions {
 	return o
 }
 
+func CreateOptionsToProto(opts ...*CreateOptions) *pb.CreateOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeCreateOptions(opts...)
+	return &pb.CreateOptions{
+		UpdateAttrs: o.UpdateAttrs,
+	}
+}
+
+func CreateOptionsFromProto(opts *pb.CreateOptions) *CreateOptions {
+	if opts == nil {
+		return nil
+	}
+	return &CreateOptions{
+		UpdateAttrs: opts.UpdateAttrs,
+	}
+}
+
 type IntrospectOptions struct {
 	Options
-	Locale string
 }
 
 type IntrospectOptionsFn func(*IntrospectOptions) *IntrospectOptions
@@ -70,6 +94,12 @@ func MergeIntrospectOptions(opts ...*IntrospectOptions) *IntrospectOptions {
 
 type GetOptions struct {
 	Options
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
 }
 
 type GetOptionsFn func(*GetOptions) *GetOptions
@@ -81,17 +111,49 @@ func MergeGetOptions(opts ...*GetOptions) *GetOptions {
 			continue
 		}
 		o.Options = MergeOptions(o.Options, opt.Options)
+		if opt.LocaleID != "" {
+			o.LocaleID = opt.LocaleID
+		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
 	}
 	return o
 }
 
+func GetOptionsToProto(opts ...*GetOptions) *pb.GetOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeGetOptions(opts...)
+	return &pb.GetOptions{
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+	}
+}
+
+func GetOptionsFromProto(opts *pb.GetOptions) *GetOptions {
+	if opts == nil {
+		return nil
+	}
+	return &GetOptions{
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+}
+
 type FindOptions struct {
 	Options
 	options.FindOptions
+
 	Deleted   bool
 	Regular   bool
 	Hidden    bool
 	Templates bool
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
 }
 
 type FindOptionsFn func(*FindOptions) *FindOptions
@@ -114,6 +176,44 @@ func MergeFindOptions(opts ...*FindOptions) *FindOptions {
 		o.Deleted = o.Deleted || opt.Deleted
 		o.Options = MergeOptions(o.Options, opt.Options)
 		o.FindOptions = *options.MergeFindOptions(&o.FindOptions, &opt.FindOptions)
+		if opt.LocaleID != "" {
+			o.LocaleID = opt.LocaleID
+		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+	}
+	return o
+}
+
+func FindOptionsToProto(opts ...*FindOptions) *pb.FindOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeFindOptions(opts...)
+	return &pb.FindOptions{
+		Deleted:         o.Deleted,
+		Regular:         o.Regular,
+		Hidden:          o.Hidden,
+		Templates:       o.Templates,
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+		Options:         options.FindOptionsToPB(&o.FindOptions),
+	}
+}
+
+func FindOptionsFromProto(opts *pb.FindOptions) *FindOptions {
+	if opts == nil {
+		return nil
+	}
+	o := &FindOptions{
+		Deleted:         opts.Deleted,
+		Regular:         opts.Regular,
+		Hidden:          opts.Hidden,
+		Templates:       opts.Templates,
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+	if fo := options.FindOptionsFromPB(opts.Options); fo != nil {
+		o.FindOptions = *fo
 	}
 	return o
 }
@@ -141,6 +241,25 @@ func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions {
 	return o
 }
 
+func UpdateOptionsToProto(opts ...*UpdateOptions) *pb.UpdateOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeUpdateOptions(opts...)
+	return &pb.UpdateOptions{
+		UpdateAttrs: o.UpdateAttrs,
+	}
+}
+
+func UpdateOptionsFromProto(opts *pb.UpdateOptions) *UpdateOptions {
+	if opts == nil {
+		return nil
+	}
+	return &UpdateOptions{
+		UpdateAttrs: opts.UpdateAttrs,
+	}
+}
+
 type DeleteOptions struct {
 	Options
 
@@ -168,6 +287,27 @@ func MergeDeleteOptions(options ...*DeleteOptions) *DeleteOptions {
 	return o
 }
 
+func DeleteOptionsToProto(opts ...*DeleteOptions) *pb.DeleteOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeDeleteOptions(opts...)
+	return &pb.DeleteOptions{
+		UpdateAttrs: o.UpdateAttrs,
+		Erase:       o.Erase,
+	}
+}
+
+func DeleteOptionsFromProto(opts *pb.DeleteOptions) *DeleteOptions {
+	if opts == nil {
+		return nil
+	}
+	return &DeleteOptions{
+		UpdateAttrs: opts.UpdateAttrs,
+		Erase:       opts.Erase,
+	}
+}
+
 type SoftDeleteOptions struct {
 	Options
 }
@@ -254,7 +394,20 @@ func MergeUnpublishOptions(opts ...*UnpublishOptions) *UnpublishOptions {
 
 type GetPublishedOptions struct {
 	Options
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
 	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
+}
+
+func (opts *GetPublishedOptions) ToGetOptions() *GetOptions {
+	return &GetOptions{
+		Options:         MergeOptions(opts.Options),
+		LocaleID:        opts.LocaleID,
+		TranslationsIDs: slices.Clone(opts.TranslationsIDs),
+	}
 }
 
 type GetPublishedOptionsFn func(*GetPublishedOptions) *GetPublishedOptions
@@ -265,6 +418,8 @@ func NewGetPublishedOptions(oo ...interface{}) *GetPublishedOptions {
 		switch o := o.(type) {
 		case string:
 			fo.LocaleID = o
+		case []string:
+			fo.TranslationsIDs = o
 		}
 	}
 	return fo
@@ -280,17 +435,59 @@ func MergeGetPublishedOptions(opts ...*GetPublishedOptions) *GetPublishedOptions
 		if opt.LocaleID != "" {
 			o.LocaleID = opt.LocaleID
 		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
 	}
 	return o
 }
 
+func GetPublishedOptionsToProto(opts ...*GetPublishedOptions) *pb.GetPublishedOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeGetPublishedOptions(opts...)
+	return &pb.GetPublishedOptions{
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+	}
+}
+
+func GetPublishedOptionsFromProto(opts *pb.GetPublishedOptions) *GetPublishedOptions {
+	if opts == nil {
+		return nil
+	}
+	return &GetPublishedOptions{
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+}
+
 type FindPublishedOptions struct {
 	Options
 	options.FindOptions
-	LocaleID  string
+
+	Deleted   bool
 	Regular   bool
 	Hidden    bool
 	Templates bool
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
+}
+
+func (opts *FindPublishedOptions) ToFindOptions() *FindOptions {
+	return &FindOptions{
+		Options:         MergeOptions(opts.Options),
+		FindOptions:     *options.MergeFindOptions(opts.FindOptions),
+		Deleted:         opts.Deleted,
+		Regular:         opts.Regular,
+		Hidden:          opts.Hidden,
+		Templates:       opts.Templates,
+		LocaleID:        opts.LocaleID,
+		TranslationsIDs: slices.Clone(opts.TranslationsIDs),
+	}
 }
 
 func NewFindPublishedOptions(opts ...interface{}) *FindPublishedOptions {
@@ -321,12 +518,51 @@ func MergeFindPublishedOptions(opts ...*FindPublishedOptions) *FindPublishedOpti
 		if opt.LocaleID != "" {
 			o.LocaleID = opt.LocaleID
 		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+	}
+	return o
+}
+
+func FindPublishedOptionsToProto(opts ...*FindPublishedOptions) *pb.FindPublishedOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeFindPublishedOptions(opts...)
+	return &pb.FindPublishedOptions{
+		Regular:         o.Regular,
+		Hidden:          o.Hidden,
+		Templates:       o.Templates,
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+		Options:         options.FindOptionsToPB(&o.FindOptions),
+	}
+}
+
+func FindPublishedOptionsFromProto(opts *pb.FindPublishedOptions) *FindPublishedOptions {
+	if opts == nil {
+		return nil
+	}
+	o := &FindPublishedOptions{
+		Regular:         opts.Regular,
+		Hidden:          opts.Hidden,
+		Templates:       opts.Templates,
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+	if fo := options.FindOptionsFromPB(opts.Options); fo != nil {
+		o.FindOptions = *fo
 	}
 	return o
 }
 
 type GetRevisionOptions struct {
 	Options
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
 }
 
 func MergeGetRevisionOptions(opts ...*GetRevisionOptions) *GetRevisionOptions {
@@ -336,6 +572,33 @@ func MergeGetRevisionOptions(opts ...*GetRevisionOptions) *GetRevisionOptions {
 			continue
 		}
 		o.Options = MergeOptions(o.Options, opt.Options)
+
+		if opt.LocaleID != "" {
+			o.LocaleID = opt.LocaleID
+		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+	}
+	return o
+}
+
+func GetRevisionOptionsToProto(opts ...*GetRevisionOptions) *pb.GetRevisionOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeGetRevisionOptions(opts...)
+	return &pb.GetRevisionOptions{
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+	}
+}
+
+func GetRevisionOptionsFromProto(opts *pb.GetRevisionOptions) *GetRevisionOptions {
+	if opts == nil {
+		return nil
+	}
+	o := &GetRevisionOptions{
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
 	}
 	return o
 }
@@ -343,6 +606,12 @@ func MergeGetRevisionOptions(opts ...*GetRevisionOptions) *GetRevisionOptions {
 type ListRevisionsOptions struct {
 	Options
 	options.FindOptions
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
 }
 
 func MergeListRevisionsOptions(opts ...*ListRevisionsOptions) *ListRevisionsOptions {
@@ -353,6 +622,37 @@ func MergeListRevisionsOptions(opts ...*ListRevisionsOptions) *ListRevisionsOpti
 		}
 		o.Options = MergeOptions(o.Options, opt.Options)
 		o.FindOptions = *options.MergeFindOptions(&o.FindOptions, &opt.FindOptions)
+
+		if opt.LocaleID != "" {
+			o.LocaleID = opt.LocaleID
+		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+	}
+	return o
+}
+
+func ListRevisionsOptionsToProto(opts ...*ListRevisionsOptions) *pb.ListRevisionsOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeListRevisionsOptions(opts...)
+	return &pb.ListRevisionsOptions{
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+		Options:         options.FindOptionsToPB(&o.FindOptions),
+	}
+}
+
+func ListRevisionsOptionsFromProto(opts *pb.ListRevisionsOptions) *ListRevisionsOptions {
+	if opts == nil {
+		return nil
+	}
+	o := &ListRevisionsOptions{
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+	if fo := options.FindOptionsFromPB(opts.Options); fo != nil {
+		o.FindOptions = *fo
 	}
 	return o
 }
@@ -375,6 +675,12 @@ func MergeArchiveOptions(opts ...*ArchiveOptions) *ArchiveOptions {
 type FindArchivedOptions struct {
 	Options
 	options.FindOptions
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
 }
 
 func NewFindArchivedOptions(oo ...interface{}) *FindArchivedOptions {
@@ -391,6 +697,37 @@ func MergeFindArchivedOptions(opts ...*FindArchivedOptions) *FindArchivedOptions
 		}
 		o.Options = MergeOptions(o.Options, opt.Options)
 		o.FindOptions = *options.MergeFindOptions(o.FindOptions, opt.FindOptions)
+
+		if opt.LocaleID != "" {
+			o.LocaleID = opt.LocaleID
+		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+	}
+	return o
+}
+
+func FindArchivedOptionsToProto(opts ...*FindArchivedOptions) *pb.FindArchivedOptions {
+	if opts == nil {
+		return nil
+	}
+	o := MergeFindArchivedOptions(opts...)
+	return &pb.FindArchivedOptions{
+		LocaleId:        o.LocaleID,
+		TranslationsIds: o.TranslationsIDs,
+		Options:         options.FindOptionsToPB(&o.FindOptions),
+	}
+}
+
+func FindArchivedOptionsFromProto(opts *pb.FindArchivedOptions) *FindArchivedOptions {
+	if opts == nil {
+		return nil
+	}
+	o := &FindArchivedOptions{
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+	if fo := options.FindOptionsFromPB(opts.Options); fo != nil {
+		o.FindOptions = *fo
 	}
 	return o
 }
@@ -448,6 +785,14 @@ func MergeAggregateOptions(opts ...*AggregateOptions) *AggregateOptions {
 
 type AggregatePublishedOptions AggregateOptions
 
+func (opts *AggregatePublishedOptions) ToAggregateOptions() *AggregateOptions {
+	return &AggregateOptions{
+		Options:     MergeOptions(opts.Options),
+		SortOptions: options.MergeSortOptions(opts.SortOptions),
+		Fields:      maps.Clone(opts.Fields),
+	}
+}
+
 func MergeAggregatePublishedOptions(opts ...*AggregatePublishedOptions) *AggregatePublishedOptions {
 	ao := make([]*AggregateOptions, len(opts))
 	for i, opt := range opts {
diff --git a/pkg/items/pagination.go b/pkg/items/pagination.go
index c8c4bb16d2da0d2d10e3d98422aaf345ffbf7c21..73a5ae9ac8e3431e332b2c1de9b56c66c78fc503 100644
--- a/pkg/items/pagination.go
+++ b/pkg/items/pagination.go
@@ -27,33 +27,26 @@ func (b *BatchProcessor) getBatch(ctx context.Context) ([]*Item, bool, error) {
 	var total int
 
 	if b.FindPublishedOptions != nil {
+		opts := *b.FindPublishedOptions
+		opts.FindOptions = *options.New(b.processed, b.limit, b.sort...)
 		res, total, err = b.Items.FindPublished(
 			ctx,
 			b.SpaceID,
 			b.EnvID,
 			b.CollectionID,
 			b.Filter,
-			&FindPublishedOptions{
-				Regular:     b.FindPublishedOptions.Regular,
-				Hidden:      b.FindPublishedOptions.Hidden,
-				Templates:   b.FindPublishedOptions.Templates,
-				FindOptions: *options.New(b.processed, b.limit, b.sort...),
-			},
+			&opts,
 		)
 	} else {
+		opts := *b.FindOptions
+		opts.FindOptions = *options.New(b.processed, b.limit, b.sort...)
 		res, total, err = b.Items.Find(
 			ctx,
 			b.SpaceID,
 			b.EnvID,
 			b.CollectionID,
 			b.Filter,
-			&FindOptions{
-				Deleted:     b.FindOptions.Deleted,
-				Regular:     b.FindOptions.Regular,
-				Hidden:      b.FindOptions.Hidden,
-				Templates:   b.FindOptions.Templates,
-				FindOptions: *options.New(b.processed, b.limit, b.sort...),
-			},
+			&opts,
 		)
 	}
 
diff --git a/pkg/items/pagination_test.go b/pkg/items/pagination_test.go
index 008b17795cc1c57d4e2dcf6bb3507bee8b3f1646..23d484b83e3e21ec3ee7b5f93477ea9d9a9b7cee 100644
--- a/pkg/items/pagination_test.go
+++ b/pkg/items/pagination_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/options"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -58,4 +59,39 @@ func TestBatchProcessor(t *testing.T) {
 		require.NoError(t, err)
 		assert.Equal(t, 1000/25, counter)
 	})
+
+	t.Run("With FindOptions2", func(t *testing.T) {
+
+		f := &FindOptions{
+			Deleted:         true,
+			Hidden:          true,
+			Regular:         true,
+			Templates:       true,
+			TranslationsIDs: []string{"*"},
+			LocaleID:        "en",
+		}
+
+		itemssvc := &Stub{
+			FindResult: func(req StubFindRequest) StubFindResult {
+				fo := req.Options[0]
+				if !assert.Equal(t, f, fo) {
+					return StubFindResult{Items: nil, Total: 0, Error: errors.New("FindOptions not equal")}
+				}
+				return StubFindResult{Items: make([]*Item, fo.Limit), Total: 25, Error: nil}
+			},
+		}
+
+		f.FindOptions = *options.New(0, 25)
+		b := &BatchProcessor{
+			Items:        itemssvc,
+			SpaceID:      "sp",
+			EnvID:        environments.DefaultEnvironment,
+			CollectionID: "col",
+			FindOptions:  f,
+		}
+
+		var counter int
+		_, err := b.Do(context.Background(), func(batch []*Item) error { counter++; return nil })
+		require.NoError(t, err)
+	})
 }
diff --git a/pkg/items/storage.go b/pkg/items/storage.go
index 64548a009369fad6e74530e2ae5f52dd013e7997..f673e92290e22cd3ab28f25f1b1d5d35ad84bdde 100644
--- a/pkg/items/storage.go
+++ b/pkg/items/storage.go
@@ -4,6 +4,7 @@ import (
 	"context"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
 )
 
 type Storage interface {
@@ -31,17 +32,17 @@ type Storage interface {
 	// CreateRevision - перенести ревизию в коллекцию Revisions
 	CreateRevision(ctx context.Context, spaceId, envId, collectionId, itemId string) error
 
-	// Update - Обновление текущей ревизии или создание новой, если в опциях передан флаг `ReplacePublishedRevision`
+	// Update - Обновление текущей ревизии
 	Update(ctx context.Context, coll *collections.Collection, item *Item, options ...*UpdateOptions) error
 
 	// Find - поиск записей по рабочим записям, коллекция 'items'
-	Find(ctx context.Context, coll *collections.Collection, filter *Filter, opts ...*FindOptions) ([]*Item, int, error)
+	Find(ctx context.Context, coll *collections.Collection, filter *Filter, opts ...*StorageFindOptions) ([]*Item, int, error)
 
 	// GetRevision - поиск одной ревизии одной записи
-	GetRevision(ctx context.Context, coll *collections.Collection, itemId, revisionId string, options ...*GetRevisionOptions) (*Item, error)
+	GetRevision(ctx context.Context, coll *collections.Collection, itemId, revisionId string, options ...*StorageFindOptions) (*Item, error)
 
 	// ListRevisions - поиск всех ревизий одной записи
-	ListRevisions(ctx context.Context, coll *collections.Collection, itemId string, options ...*ListRevisionsOptions) ([]*Item, error)
+	ListRevisions(ctx context.Context, coll *collections.Collection, itemId string, options ...*StorageFindOptions) ([]*Item, error)
 
 	// ChangeRevisionsItemID - заменить ID элемента у его ревизий
 	ChangeRevisionsItemID(ctx context.Context, spaceId, envId, collectionId, itemId, newItemId string) error
@@ -53,7 +54,7 @@ type Storage interface {
 	Unpublish(ctx context.Context, unpublished *Item, options ...*UnpublishOptions) error
 
 	// FindPublished - поиск по опубликованным записям, коллекция 'items_published'
-	FindPublished(ctx context.Context, coll *collections.Collection, filter *Filter, opts ...*FindPublishedOptions) ([]*Item, int, error)
+	FindPublished(ctx context.Context, coll *collections.Collection, filter *Filter, opts ...*StorageFindOptions) ([]*Item, int, error)
 
 	// Archive - архивация записи
 	Archive(ctx context.Context, archived *Item, options ...*ArchiveOptions) error
@@ -62,7 +63,7 @@ type Storage interface {
 	Unarchive(ctx context.Context, unarchived *Item, options ...*UnarchiveOptions) error
 
 	// FindArchived - поиск по архивированным записям, коллекция 'items_archived'
-	FindArchived(ctx context.Context, coll *collections.Collection, filter *Filter, opts ...*FindArchivedOptions) ([]*Item, int, error)
+	FindArchived(ctx context.Context, coll *collections.Collection, filter *Filter, opts ...*StorageFindOptions) ([]*Item, int, error)
 
 	// RemoveItems - удаление записи из коллекций Items
 	RemoveItems(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*DeleteOptions) error
@@ -70,6 +71,9 @@ type Storage interface {
 	// RemovePublished - удаление записи из коллекций Published
 	RemovePublished(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*DeleteOptions) error
 
+	// CleanUpRevisions - очистка ревизий элемента, согласно настройкам лимитов для ревизий в коллекции
+	CleanUpRevisions(ctx context.Context, coll *collections.Collection, itemId string) error
+
 	// RemoveRevisions - удаление записи из всех ревизий элемента
 	RemoveRevisions(ctx context.Context, spaceId, envId, collectionId, itemId string, options ...*DeleteOptions) error
 
@@ -83,7 +87,7 @@ type Storage interface {
 	Copy(ctx context.Context, src, dst *collections.Collection, itemSets ...string) error
 
 	Reset(ctx context.Context, spaceId, envId, collectionId string) error
-	Init(ctx context.Context, collection *collections.Collection) error
+	Init(ctx context.Context, collection *collections.Collection, locales ...*locales.Locale) error
 
 	// Aggregate выполняет агрегацию данных
 	Aggregate(ctx context.Context, coll *collections.Collection, filter *Filter, options ...*AggregateOptions) (result map[string]interface{}, err error)
diff --git a/pkg/items/storage_options.go b/pkg/items/storage_options.go
new file mode 100644
index 0000000000000000000000000000000000000000..a56232fb22b2cac5a8555952b5898bf229fc875a
--- /dev/null
+++ b/pkg/items/storage_options.go
@@ -0,0 +1,94 @@
+package items
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+)
+
+type StorageFindOptions struct {
+	Options
+	options.FindOptions
+
+	Deleted   bool
+	Regular   bool
+	Hidden    bool
+	Templates bool
+
+	// Язык перевода, который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	LocaleID string
+
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	TranslationsIDs []string
+
+	// Список локалей пространства
+	Locales []*locales.Locale
+}
+
+func NewStorageFindOptions(opts ...interface{}) *StorageFindOptions {
+	fo := &StorageFindOptions{}
+	for _, o := range opts {
+		switch o := o.(type) {
+		case *FindOptions:
+			fo.Options = o.Options
+			fo.FindOptions = o.FindOptions
+			fo.Deleted = o.Deleted
+			fo.Regular = o.Regular
+			fo.Hidden = o.Hidden
+			fo.Templates = o.Templates
+			fo.LocaleID = o.LocaleID
+			fo.TranslationsIDs = o.TranslationsIDs
+		case *FindPublishedOptions:
+			fo.Options = o.Options
+			fo.FindOptions = o.FindOptions
+			fo.Deleted = o.Deleted
+			fo.Regular = o.Regular
+			fo.Hidden = o.Hidden
+			fo.Templates = o.Templates
+			fo.LocaleID = o.LocaleID
+			fo.TranslationsIDs = o.TranslationsIDs
+		case *FindArchivedOptions:
+			fo.Options = o.Options
+			fo.FindOptions = o.FindOptions
+			fo.LocaleID = o.LocaleID
+			fo.TranslationsIDs = o.TranslationsIDs
+		case *GetRevisionOptions:
+			fo.Options = o.Options
+			fo.LocaleID = o.LocaleID
+			fo.TranslationsIDs = o.TranslationsIDs
+		case *ListRevisionsOptions:
+			fo.Options = o.Options
+			fo.FindOptions = o.FindOptions
+			fo.LocaleID = o.LocaleID
+			fo.TranslationsIDs = o.TranslationsIDs
+		case *locales.Locale:
+			fo.Locales = []*locales.Locale{o}
+		case []*locales.Locale:
+			fo.Locales = o
+		}
+	}
+	return fo
+}
+
+func MergeStorageFindOptions(opts ...*StorageFindOptions) *StorageFindOptions {
+	o := NewStorageFindOptions()
+	for _, opt := range opts {
+		if opt == nil {
+			continue
+		}
+
+		o.Options = MergeOptions(o.Options, opt.Options)
+		o.FindOptions = *options.MergeFindOptions(&o.FindOptions, &opt.FindOptions)
+
+		o.Deleted = o.Deleted || opt.Deleted
+		o.Regular = o.Regular || opt.Regular
+		o.Templates = o.Templates || opt.Templates
+		o.Hidden = o.Hidden || opt.Hidden
+
+		if opt.LocaleID != "" {
+			o.LocaleID = opt.LocaleID
+		}
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+		o.Locales = append(o.Locales, opt.Locales...)
+	}
+	return o
+}
diff --git a/pkg/items/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/items/transport/grpc/protobuf_endpoint_converters.microgen.go
index b111a6160dba31fb7adb2919a66d8e3bf620ef16..47208ce50e9e869b21c5db611ad89fa66eef8289 100644
--- a/pkg/items/transport/grpc/protobuf_endpoint_converters.microgen.go
+++ b/pkg/items/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -7,6 +7,7 @@ import (
 	"context"
 	"errors"
 
+	"git.perx.ru/perxis/perxis-go/pkg/items"
 	transport "git.perx.ru/perxis/perxis-go/pkg/items/transport"
 	pb "git.perx.ru/perxis/perxis-go/proto/items"
 	empty "google.golang.org/protobuf/types/known/emptypb"
@@ -37,11 +38,16 @@ func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{},
 		return nil, errors.New("nil GetRequest")
 	}
 	req := request.(*transport.GetRequest)
+	opts, err := GetOptionsToProto(req.Options)
+	if err != nil {
+		return nil, err
+	}
 	return &pb.GetRequest{
 		CollectionId: req.CollectionId,
 		EnvId:        req.EnvId,
 		ItemId:       req.ItemId,
 		SpaceId:      req.SpaceId,
+		Options:      opts,
 	}, nil
 }
 
@@ -54,15 +60,11 @@ func _Encode_Find_Request(ctx context.Context, request interface{}) (interface{}
 	if err != nil {
 		return nil, err
 	}
-	reqOptions, err := ElPtrFindOptionsToProto(req.Options)
-	if err != nil {
-		return nil, err
-	}
 	return &pb.FindRequest{
 		CollectionId: req.CollectionId,
 		EnvId:        req.EnvId,
 		SpaceId:      req.SpaceId,
-		Options:      reqOptions,
+		Options:      items.FindOptionsToProto(req.Options...),
 		Filter:       reqFilter,
 	}, nil
 }
@@ -212,12 +214,17 @@ func _Encode_GetRevision_Request(ctx context.Context, request interface{}) (inte
 		return nil, errors.New("nil GetRevisionRequest")
 	}
 	req := request.(*transport.GetRevisionRequest)
+	opts, err := GetRevisionOptionsToProto(req.Options)
+	if err != nil {
+		return nil, err
+	}
 	return &pb.GetRevisionRequest{
 		CollectionId: req.CollectionId,
 		EnvId:        req.EnvId,
 		ItemId:       req.ItemId,
 		RevisionId:   req.RevisionId,
 		SpaceId:      req.SpaceId,
+		Options:      opts,
 	}, nil
 }
 
@@ -226,7 +233,7 @@ func _Encode_ListRevisions_Request(ctx context.Context, request interface{}) (in
 		return nil, errors.New("nil ListRevisionsRequest")
 	}
 	req := request.(*transport.ListRevisionsRequest)
-	reqOptions, err := ElPtrListRevisionsOptionsToProto(req.Options)
+	reqOptions, err := ListRevisionsOptionsToProto(req.Options)
 	if err != nil {
 		return nil, err
 	}
@@ -260,7 +267,7 @@ func _Encode_FindArchived_Request(ctx context.Context, request interface{}) (int
 	if err != nil {
 		return nil, err
 	}
-	reqOptions, err := ElPtrFindArchivedOptionsToProto(req.Options)
+	reqOptions, err := FindArchivedOptionsToProto(req.Options)
 	if err != nil {
 		return nil, err
 	}
@@ -515,11 +522,16 @@ func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{},
 		return nil, errors.New("nil GetRequest")
 	}
 	req := request.(*pb.GetRequest)
+	opts, err := ProtoToGetOptions(req.Options)
+	if err != nil {
+		return nil, err
+	}
 	return &transport.GetRequest{
 		CollectionId: string(req.CollectionId),
 		EnvId:        string(req.EnvId),
 		ItemId:       string(req.ItemId),
 		SpaceId:      string(req.SpaceId),
+		Options:      opts,
 	}, nil
 }
 
@@ -734,12 +746,17 @@ func _Decode_GetRevision_Request(ctx context.Context, request interface{}) (inte
 		return nil, errors.New("nil GetRevisionRequest")
 	}
 	req := request.(*pb.GetRevisionRequest)
+	opts, err := ProtoToGetRevisionOptions(req.Options)
+	if err != nil {
+		return nil, err
+	}
 	return &transport.GetRevisionRequest{
 		CollectionId: string(req.CollectionId),
 		EnvId:        string(req.EnvId),
 		ItemId:       string(req.ItemId),
 		RevisionId:   string(req.RevisionId),
 		SpaceId:      string(req.SpaceId),
+		Options:      opts,
 	}, nil
 }
 
@@ -748,7 +765,7 @@ func _Decode_ListRevisions_Request(ctx context.Context, request interface{}) (in
 		return nil, errors.New("nil ListRevisionsRequest")
 	}
 	req := request.(*pb.ListRevisionsRequest)
-	reqOptions, err := ProtoToElPtrListRevisionsOptions(req.Options)
+	reqOptions, err := ProtoToListRevisionsOptions(req.Options)
 	if err != nil {
 		return nil, err
 	}
@@ -782,7 +799,7 @@ func _Decode_FindArchived_Request(ctx context.Context, request interface{}) (int
 	if err != nil {
 		return nil, err
 	}
-	reqOptions, err := ProtoToElPtrFindArchivedOptions(req.Options)
+	reqOptions, err := ProtoToFindArchivedOptions(req.Options)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/items/transport/grpc/protobuf_type_converters.microgen.go b/pkg/items/transport/grpc/protobuf_type_converters.microgen.go
index ade05bfffeb4a1c745f80338e7faac2287b067a2..1aba486580e9f94882b29d254b1fffab39c8270c 100644
--- a/pkg/items/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/items/transport/grpc/protobuf_type_converters.microgen.go
@@ -176,80 +176,43 @@ func ProtoToListPtrItem(protoItems []*pb.Item) ([]*service.Item, error) {
 }
 
 func ProtoToCreateOptions(protoOptions *pb.CreateOptions) ([]*service.CreateOptions, error) {
-	if protoOptions == nil {
-		return nil, nil
-	}
-	return []*service.CreateOptions{
-		{UpdateAttrs: protoOptions.UpdateAttrs},
-	}, nil
+	return []*service.CreateOptions{service.CreateOptionsFromProto(protoOptions)}, nil
 }
 
 func CreateOptionsToProto(options []*service.CreateOptions) (*pb.CreateOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeCreateOptions(options...)
-
-	return &pb.CreateOptions{
-		UpdateAttrs: opts.UpdateAttrs,
-	}, nil
+	return service.CreateOptionsToProto(options...), nil
 }
 
-func ElPtrGetOptionsToProto() {
-	panic("function not provided") // TODO: provide converter
+func GetRevisionOptionsToProto(options []*service.GetRevisionOptions) (*pb.GetRevisionOptions, error) {
+	return service.GetRevisionOptionsToProto(options...), nil
 }
 
-func ProtoToElPtrGetOptions() {
-	panic("function not provided") // TODO: provide converter
+func ProtoToGetRevisionOptions(protoOptions *pb.GetRevisionOptions) ([]*service.GetRevisionOptions, error) {
+	return []*service.GetRevisionOptions{service.GetRevisionOptionsFromProto(protoOptions)}, nil
 }
 
-func ElPtrFindOptionsToProto(options []*service.FindOptions) (*pb.FindOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeFindOptions(options...)
+func ListRevisionsOptionsToProto(options []*service.ListRevisionsOptions) (*pb.ListRevisionsOptions, error) {
+	return service.ListRevisionsOptionsToProto(options...), nil
+}
 
-	var err error
+func ProtoToListRevisionsOptions(protoOptions *pb.ListRevisionsOptions) ([]*service.ListRevisionsOptions, error) {
+	return []*service.ListRevisionsOptions{service.ListRevisionsOptionsFromProto(protoOptions)}, nil
+}
 
-	fo := &pb.FindOptions{
-		Deleted:   opts.Deleted,
-		Regular:   opts.Regular,
-		Hidden:    opts.Hidden,
-		Templates: opts.Templates,
-	}
+func GetOptionsToProto(options []*service.GetOptions) (*pb.GetOptions, error) {
+	return service.GetOptionsToProto(options...), nil
+}
 
-	fo.Options, err = PtrServicesFindOptionsToProto(&opts.FindOptions)
-	if err != nil {
-		return nil, err
-	}
+func ProtoToGetOptions(protoOptions *pb.GetOptions) ([]*service.GetOptions, error) {
+	return []*service.GetOptions{service.GetOptionsFromProto(protoOptions)}, nil
+}
 
-	return fo, nil
+func ElPtrFindOptionsToProto(options []*service.FindOptions) (*pb.FindOptions, error) {
+	return service.FindOptionsToProto(options...), nil
 }
 
 func ProtoToElPtrFindOptions(protoOptions *pb.FindOptions) ([]*service.FindOptions, error) {
-	if protoOptions == nil {
-		return nil, nil
-	}
-
-	var err error
-	fo := &service.FindOptions{
-		Deleted:   protoOptions.Deleted,
-		Regular:   protoOptions.Regular,
-		Hidden:    protoOptions.Hidden,
-		Templates: protoOptions.Templates,
-	}
-
-	o, err := ProtoToPtrServicesFindOptions(protoOptions.Options)
-	if err != nil {
-		return nil, err
-	}
-	if o != nil {
-		fo.FindOptions = *o
-	}
-
-	return []*service.FindOptions{fo}, nil
+	return []*service.FindOptions{service.FindOptionsFromProto(protoOptions)}, nil
 }
 
 func ProtoToUpdateOptions(protoOptions *pb.UpdateOptions) ([]*service.UpdateOptions, error) {
@@ -262,40 +225,18 @@ func ProtoToUpdateOptions(protoOptions *pb.UpdateOptions) ([]*service.UpdateOpti
 }
 
 func UpdateOptionsToProto(options []*service.UpdateOptions) (*pb.UpdateOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeUpdateOptions(options...)
-
-	return &pb.UpdateOptions{
-		UpdateAttrs: opts.UpdateAttrs,
-	}, nil
+	return service.UpdateOptionsToProto(options...), nil
 }
 
 func ProtoToDeleteOptions(protoOptions *pb.DeleteOptions) ([]*service.DeleteOptions, error) {
 	if protoOptions == nil {
 		return nil, nil
 	}
-	return []*service.DeleteOptions{
-		{
-			UpdateAttrs: protoOptions.UpdateAttrs,
-			Erase:       protoOptions.Erase,
-		},
-	}, nil
+	return []*service.DeleteOptions{service.DeleteOptionsFromProto(protoOptions)}, nil
 }
 
 func DeleteOptionsToProto(options []*service.DeleteOptions) (*pb.DeleteOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeDeleteOptions(options...)
-
-	return &pb.DeleteOptions{
-		UpdateAttrs: opts.UpdateAttrs,
-		Erase:       opts.Erase,
-	}, nil
+	return service.DeleteOptionsToProto(options...), nil
 }
 
 func ProtoToUndeleteOptions(protoOptions *pb.UndeleteOptions) ([]*service.UndeleteOptions, error) {
@@ -363,70 +304,19 @@ func UnpublishOptionsToProto(options []*service.UnpublishOptions) (*pb.Unpublish
 }
 
 func ElPtrGetPublishedOptionsToProto(options []*service.GetPublishedOptions) (*pb.GetPublishedOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeGetPublishedOptions(options...)
-
-	return &pb.GetPublishedOptions{LocaleId: opts.LocaleID}, nil
+	return service.GetPublishedOptionsToProto(options...), nil
 }
 
 func ProtoToElPtrGetPublishedOptions(protoOptions *pb.GetPublishedOptions) ([]*service.GetPublishedOptions, error) {
-	if protoOptions == nil {
-		return nil, nil
-	}
-
-	return []*service.GetPublishedOptions{{LocaleID: protoOptions.LocaleId}}, nil
+	return []*service.GetPublishedOptions{service.GetPublishedOptionsFromProto(protoOptions)}, nil
 }
 
 func ElPtrFindPublishedOptionsToProto(options []*service.FindPublishedOptions) (*pb.FindPublishedOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeFindPublishedOptions(options...)
-
-	var err error
-
-	fo := &pb.FindPublishedOptions{
-		Regular:   opts.Regular,
-		Hidden:    opts.Hidden,
-		Templates: opts.Templates,
-	}
-	fo.Options, err = PtrServicesFindOptionsToProto(&opts.FindOptions)
-	if err != nil {
-		return nil, err
-	}
-
-	fo.LocaleId = opts.LocaleID
-
-	return fo, nil
+	return service.FindPublishedOptionsToProto(options...), nil
 }
 
 func ProtoToElPtrFindPublishedOptions(protoOptions *pb.FindPublishedOptions) ([]*service.FindPublishedOptions, error) {
-	if protoOptions == nil {
-		return nil, nil
-	}
-
-	var err error
-	fo := &service.FindPublishedOptions{
-		Regular:   protoOptions.Regular,
-		Hidden:    protoOptions.Hidden,
-		Templates: protoOptions.Templates,
-	}
-
-	o, err := ProtoToPtrServicesFindOptions(protoOptions.Options)
-	if err != nil {
-		return nil, err
-	}
-	if o != nil {
-		fo.FindOptions = *o
-	}
-
-	fo.LocaleID = protoOptions.LocaleId
-
-	return []*service.FindPublishedOptions{fo}, nil
+	return []*service.FindPublishedOptions{service.FindPublishedOptionsFromProto(protoOptions)}, nil
 }
 
 func ElPtrGetRevisionOptionsToProto() {
@@ -437,44 +327,6 @@ func ProtoToElPtrGetRevisionOptions() {
 	panic("function not provided") // TODO: provide converter
 }
 
-func ElPtrListRevisionsOptionsToProto(options []*service.ListRevisionsOptions) (*pb.ListRevisionsOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeListRevisionsOptions(options...)
-
-	var err error
-
-	fo := &pb.ListRevisionsOptions{}
-
-	fo.Options, err = PtrServicesFindOptionsToProto(&opts.FindOptions)
-	if err != nil {
-		return nil, err
-	}
-
-	return fo, nil
-}
-
-func ProtoToElPtrListRevisionsOptions(protoOptions *pb.ListRevisionsOptions) ([]*service.ListRevisionsOptions, error) {
-	if protoOptions == nil {
-		return nil, nil
-	}
-
-	var err error
-	fo := &service.ListRevisionsOptions{}
-
-	o, err := ProtoToPtrServicesFindOptions(protoOptions.Options)
-	if err != nil {
-		return nil, err
-	}
-	if o != nil {
-		fo.FindOptions = *o
-	}
-
-	return []*service.ListRevisionsOptions{fo}, nil
-}
-
 func ElPtrArchiveOptionsToProto() {
 	panic("function not provided") // TODO: provide converter
 }
@@ -483,42 +335,12 @@ func ProtoToElPtrArchiveOptions() {
 	panic("function not provided") // TODO: provide converter
 }
 
-func ElPtrFindArchivedOptionsToProto(options []*service.FindArchivedOptions) (*pb.FindArchivedOptions, error) {
-	if options == nil {
-		return nil, nil
-	}
-
-	opts := service.MergeFindArchivedOptions(options...)
-
-	var err error
-
-	fo := &pb.FindArchivedOptions{}
-
-	fo.Options, err = PtrServicesFindOptionsToProto(&opts.FindOptions)
-	if err != nil {
-		return nil, err
-	}
-
-	return fo, nil
+func FindArchivedOptionsToProto(options []*service.FindArchivedOptions) (*pb.FindArchivedOptions, error) {
+	return service.FindArchivedOptionsToProto(options...), nil
 }
 
-func ProtoToElPtrFindArchivedOptions(protoOptions *pb.FindArchivedOptions) ([]*service.FindArchivedOptions, error) {
-	if protoOptions == nil {
-		return nil, nil
-	}
-
-	var err error
-	fo := &service.FindArchivedOptions{}
-
-	o, err := ProtoToPtrServicesFindOptions(protoOptions.Options)
-	if err != nil {
-		return nil, err
-	}
-	if o != nil {
-		fo.FindOptions = *o
-	}
-
-	return []*service.FindArchivedOptions{fo}, nil
+func ProtoToFindArchivedOptions(protoOptions *pb.FindArchivedOptions) ([]*service.FindArchivedOptions, error) {
+	return []*service.FindArchivedOptions{service.FindArchivedOptionsFromProto(protoOptions)}, nil
 }
 
 func ElPtrUnarchiveOptionsToProto() {
diff --git a/pkg/locales/errors.go b/pkg/locales/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..b5876aff89925f394b8e36c1e609dfc6b4581164
--- /dev/null
+++ b/pkg/locales/errors.go
@@ -0,0 +1,22 @@
+package locales
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/service"
+)
+
+var (
+	ErrNotFound                       = service.ErrNotFound
+	ErrAlreadyExists                  = service.ErrAlreadyExists
+	ErrSpaceIDRequired                = errors.New("space id is required")
+	ErrLocaleValueEmpty               = errors.New("locale value cannot be empty")
+	ErrLocaleIDRequired               = errors.New("locale id is required")
+	ErrLocaleCodeRequired             = errors.New("locale code is required")
+	ErrSetSelfAsFallback              = errors.New("cannot set self as fallback")
+	ErrDisableDefaultLocale           = errors.New("cannot disable default locale")
+	ErrDisablePublishingDefaultLocale = errors.New("cannot disable publishing for default locale")
+	ErrSetDefaultLocaleFallback       = errors.New("cannot set fallback for default locale")
+	ErrDeleteDefaultLocale            = errors.New("cannot delete default locale")
+	ErrDeleteFallbackLocale           = errors.New("cannot delete locale that is used as fallback for other locales")
+	ErrCircularDependency             = errors.New("fallback locales contain cycles")
+)
diff --git a/pkg/locales/events.go b/pkg/locales/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..fe9c8d95ed4be12f129c17ec8a6cedb672024f15
--- /dev/null
+++ b/pkg/locales/events.go
@@ -0,0 +1,7 @@
+package locales
+
+const (
+	EventCreate = "locales.create"
+	EventUpdate = "locales.update"
+	EventDelete = "locales.delete"
+)
diff --git a/pkg/locales/locale.go b/pkg/locales/locale.go
index d3cc43c6c625a5090d975409d7aa2beb16eefc3a..e0e32616ee4bd526c7d25c9cbab740e196529328 100644
--- a/pkg/locales/locale.go
+++ b/pkg/locales/locale.go
@@ -1,7 +1,164 @@
 package locales
 
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/data"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	pb "git.perx.ru/perxis/perxis-go/proto/locales"
+	"golang.org/x/text/language"
+)
+
+const (
+	DefaultID        = "default" // DefaultID идентификатор локали по умолчанию
+	DefaultDirection = "ltr"     // DefaultDirection направление письма по умолчанию
+)
+
 type Locale struct {
-	ID      string `json:"id" bson:"_id"` // (Пример: "en", "en-US")
-	SpaceID string `json:"spaceId" bson:"-"`
-	Name    string `json:"name" bson:"name"` // (Пример: "English", "English (US)" )
+	ID         string `json:"id" bson:"_id"`                  // Идентификатор локали, генерируется автоматически. Для локали по умолчанию устанавливается как "default".
+	SpaceID    string `json:"spaceId" bson:"-"`               // Идентификатор пространства.
+	Name       string `json:"name" bson:"name"`               // Название локали. Опционально, заполняется автоматически (Пример: russian, english)
+	NativeName string `json:"native_name" bson:"native_name"` // Название локали на языке локали. Опционально, заполняется автоматически (Пример: Русский, English)
+	Code       string `json:"code" bson:"code"`               // Код локали https://en.wikipedia.org/wiki/IETF_language_tag
+	Fallback   string `json:"fallback" bson:"fallback"`       // Идентификатор локали, который будет использоваться при отсутствии перевода
+	Direction  string `json:"direction" bson:"direction"`     // Направление письма - слева направо (ltr) или справа налево (rtl). По умолчанию устанавливается ltr.
+	Weight     int    `json:"weight" bson:"weight"`           // Вес локали.
+	NoPublish  bool   `json:"no_publish" bson:"no_publish"`   // Не публиковать контент данной локали. Не будет доступен контент через Delivery API. (кроме default)
+	Disabled   bool   `json:"disabled" bson:"disabled"`       // Запретить использование локали. Нельзя создавать и редактировать контент для данной локали (кроме default)
+}
+
+func (locale *Locale) Clone() *Locale {
+	return &Locale{
+		ID:         locale.ID,
+		SpaceID:    locale.SpaceID,
+		Name:       locale.Name,
+		NativeName: locale.NativeName,
+		Code:       locale.Code,
+		Fallback:   locale.Fallback,
+		Direction:  locale.Direction,
+		Weight:     locale.Weight,
+		NoPublish:  locale.NoPublish,
+		Disabled:   locale.Disabled,
+	}
+}
+
+func (locale *Locale) IsDefault() bool {
+	return locale.ID == DefaultID
+}
+
+func (locale *Locale) Equal(other *Locale) bool {
+	return locale == other || locale != nil && other != nil && *locale == *other
+}
+
+func (locale *Locale) Key() string {
+	if locale == nil {
+		return ""
+	}
+	return locale.ID
+}
+
+// Возвращает язык локали, например "en", "ru"
+func (locale *Locale) GetLanguage() string {
+	lang, err := language.Parse(locale.Code)
+	if err != nil {
+		return ""
+	}
+
+	base, _ := lang.Base()
+	return base.String()
+}
+
+func GetIDs(locales []*Locale) []string {
+	result := make([]string, 0, len(locales))
+	for _, locale := range locales {
+		result = append(result, locale.ID)
+	}
+	return result
+}
+
+func LocaleToProto(locale *Locale) *pb.Locale {
+	if locale == nil {
+		return nil
+	}
+	protoLocale := &pb.Locale{
+		Id:         locale.ID,
+		SpaceId:    locale.SpaceID,
+		Name:       locale.Name,
+		NativeName: locale.NativeName,
+		Code:       locale.Code,
+		Fallback:   locale.Fallback,
+		Direction:  locale.Direction,
+		Weight:     int64(locale.Weight),
+		NoPublish:  locale.NoPublish,
+		Disabled:   locale.Disabled,
+	}
+	return protoLocale
+}
+
+func LocaleFromProto(protoLocale *pb.Locale) *Locale {
+	if protoLocale == nil {
+		return nil
+	}
+	locale := &Locale{
+		ID:         protoLocale.Id,
+		SpaceID:    protoLocale.SpaceId,
+		Name:       protoLocale.Name,
+		NativeName: protoLocale.NativeName,
+		Code:       protoLocale.Code,
+		Fallback:   protoLocale.Fallback,
+		Direction:  protoLocale.Direction,
+		Weight:     int(protoLocale.Weight),
+		NoPublish:  protoLocale.NoPublish,
+		Disabled:   protoLocale.Disabled,
+	}
+	return locale
+}
+
+func FallbackSort(locales []*Locale) ([]*Locale, error) {
+	if len(locales) == 0 {
+		return locales, nil
+	}
+
+	var (
+		unsorted = data.SliceToMap(locales)
+		sorted   = make([]*Locale, 0, len(locales))
+		visited  = make(map[string]bool, len(locales))
+	)
+
+	for {
+		changed := false
+		for _, node := range locales {
+
+			if visited[node.ID] {
+				continue
+			}
+
+			if _, exist := unsorted[node.Fallback]; !exist && node.Fallback != "" {
+				return nil, errors.Wrap(ErrNotFound, node.Fallback)
+			}
+
+			if node.IsDefault() {
+				sorted = append([]*Locale{node}, sorted...)
+				visited[node.ID] = true
+				changed = true
+				continue
+			}
+
+			if node.Fallback == "" || visited[node.Fallback] {
+				sorted = append(sorted, node)
+				visited[node.ID] = true
+				changed = true
+			}
+		}
+
+		// Все локали перенесены в список отсортированных
+		if len(sorted) == len(locales) {
+			break
+		}
+
+		// Если не произошло изменений, но не все локали отсортированы - обнаружены циклические зависимости
+		if !changed {
+			return nil, ErrCircularDependency
+		}
+	}
+
+	return sorted, nil
 }
diff --git a/pkg/locales/locale_test.go b/pkg/locales/locale_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..664e369277d701e8c34b818433c492ee688695f4
--- /dev/null
+++ b/pkg/locales/locale_test.go
@@ -0,0 +1,297 @@
+package locales
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestLocale_GetLanguage(t *testing.T) {
+	tests := []struct {
+		code     string
+		wantLang string
+	}{
+		{
+			code:     "ru",
+			wantLang: "ru",
+		},
+		{
+			code:     "ru_RU",
+			wantLang: "ru",
+		},
+		{
+			code:     "en-US",
+			wantLang: "en",
+		},
+		{
+			code:     "incorrect-code",
+			wantLang: "",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.code, func(t *testing.T) {
+			locale := &Locale{Code: tt.code}
+			require.Equal(t, tt.wantLang, locale.GetLanguage())
+		})
+	}
+}
+
+func TestLocale_Equal(t *testing.T) {
+	tests := []struct {
+		locale *Locale
+		other  *Locale
+		want   bool
+	}{
+		{
+			locale: &Locale{Code: "ru-RU"},
+			other:  &Locale{Code: "ru-RU"},
+			want:   true,
+		},
+		{
+			locale: &Locale{Code: "ru-RU"},
+			other:  &Locale{Code: "ru-RU", Fallback: "en-US"},
+			want:   false,
+		},
+		{
+			locale: &Locale{Code: "ru-RU", Fallback: "en-US"},
+			other:  &Locale{Code: "ru-RU"},
+			want:   false,
+		},
+		{
+			locale: &Locale{Code: "ru-RU"},
+			other:  nil,
+			want:   false,
+		},
+		{
+			locale: nil,
+			other:  &Locale{Code: "ru-RU"},
+			want:   false,
+		},
+		{
+			locale: nil,
+			other:  nil,
+			want:   true,
+		},
+	}
+	for _, tt := range tests {
+		got := tt.locale.Equal(tt.other)
+		require.Equal(t, tt.want, got)
+	}
+}
+
+func TestFallbackSort(t *testing.T) {
+	tests := []struct {
+		name    string
+		input   []*Locale
+		want    []*Locale
+		wantErr bool
+	}{
+		{
+			name:    "Empty input",
+			input:   nil,
+			want:    nil,
+			wantErr: false,
+		},
+		{
+			name:    "Empty input",
+			input:   []*Locale{},
+			want:    []*Locale{},
+			wantErr: false,
+		},
+		{
+			name: "One locale not default",
+			input: []*Locale{
+				{ID: "a"},
+			},
+			want: []*Locale{
+				{ID: "a"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "One default locale",
+			input: []*Locale{
+				{ID: DefaultID},
+			},
+			want: []*Locale{
+				{ID: DefaultID},
+			},
+			wantErr: false,
+		},
+		{
+			name: "One locale with fallback",
+			input: []*Locale{
+				{ID: DefaultID, Fallback: "a"},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Fallback locale not exists",
+			input: []*Locale{
+				{ID: "a", Fallback: "c"},
+				{ID: "b", Fallback: DefaultID},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "Not found default",
+			input: []*Locale{
+				{ID: "a", Fallback: DefaultID},
+				{ID: "b", Fallback: "d"},
+				{ID: "c", Fallback: DefaultID},
+				{ID: "d", Fallback: "c"},
+				{ID: "e", Fallback: "a"},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Simple",
+			input: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: DefaultID},
+				{ID: "b", Fallback: "d"},
+				{ID: "c", Fallback: DefaultID},
+				{ID: "d", Fallback: "c"},
+				{ID: "e", Fallback: "a"},
+			},
+			want: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: DefaultID},
+				{ID: "c", Fallback: DefaultID},
+				{ID: "d", Fallback: "c"},
+				{ID: "e", Fallback: "a"},
+				{ID: "b", Fallback: "d"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "Simple #2",
+			input: []*Locale{
+				{ID: "a", Fallback: DefaultID},
+				{ID: "b", Fallback: "d"},
+				{ID: "c", Fallback: DefaultID},
+				{ID: "d", Fallback: "c"},
+				{ID: "e", Fallback: "a"},
+				{ID: DefaultID},
+			},
+			want: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: DefaultID},
+				{ID: "c", Fallback: DefaultID},
+				{ID: "d", Fallback: "c"},
+				{ID: "e", Fallback: "a"},
+				{ID: "b", Fallback: "d"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "Simple #3",
+			input: []*Locale{
+				{ID: DefaultID},
+				{ID: "a"},
+				{ID: "b"},
+			},
+			want: []*Locale{
+				{ID: DefaultID},
+				{ID: "a"},
+				{ID: "b"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "Simple #4",
+			input: []*Locale{
+				{ID: "a"},
+				{ID: "b"},
+				{ID: DefaultID},
+			},
+			want: []*Locale{
+				{ID: DefaultID},
+				{ID: "a"},
+				{ID: "b"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "Simple #5",
+			input: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: "b"},
+				{ID: "b", Fallback: "c"},
+				{ID: "c"},
+			},
+			want: []*Locale{
+				{ID: DefaultID},
+				{ID: "c"},
+				{ID: "b", Fallback: "c"},
+				{ID: "a", Fallback: "b"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "Simple #6",
+			input: []*Locale{
+				{ID: "a", Fallback: "b"},
+				{ID: "b", Fallback: "c"},
+				{ID: "c"},
+			},
+			want: []*Locale{
+				{ID: "c"},
+				{ID: "b", Fallback: "c"},
+				{ID: "a", Fallback: "b"},
+			},
+			wantErr: false,
+		},
+		{
+			name: "Cyclical dependency",
+			input: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: "b"},
+				{ID: "b", Fallback: "c"},
+				{ID: "c", Fallback: "a"},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "Cyclical dependency #2",
+			input: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: DefaultID},
+				{ID: "b", Fallback: "d"},
+				{ID: "c", Fallback: "b"},
+				{ID: "d", Fallback: "c"},
+				{ID: "e", Fallback: "a"},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "Cyclical dependency #3",
+			input: []*Locale{
+				{ID: DefaultID},
+				{ID: "a", Fallback: "b"},
+				{ID: "b", Fallback: "a"},
+				{ID: "c", Fallback: "d"},
+				{ID: "d", Fallback: "c"},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := FallbackSort(tt.input)
+			if tt.wantErr {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/pkg/locales/middleware/access_logging_middleware.go b/pkg/locales/middleware/access_logging_middleware.go
index eabb71c37756d0a7d39d6abe217f394036a69922..ab31fc8c81ec51af35e63ab8eadbc400a9b717db 100644
--- a/pkg/locales/middleware/access_logging_middleware.go
+++ b/pkg/locales/middleware/access_logging_middleware.go
@@ -87,3 +87,21 @@ func (m *accessLoggingMiddleware) List(ctx context.Context, spaceId string) (loc
 
 	return locales, err
 }
+
+func (m *accessLoggingMiddleware) Update(ctx context.Context, locale *locales.Locale) (err error) {
+	begin := time.Now()
+
+	m.logger.Debug("Update.Request",
+		zap.Reflect("principal", auth.GetPrincipal(ctx)),
+		zap.Reflect("locale", locale),
+	)
+
+	err = m.next.Update(ctx, locale)
+
+	m.logger.Debug("Update.Response",
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	)
+
+	return err
+}
diff --git a/pkg/locales/middleware/caching_middleware.go b/pkg/locales/middleware/caching_middleware.go
index 13928a5166f2187e85a5e7da63861509e3f69d3a..64f82bc2347323140674cb7375bfe41a36a5bfb0 100644
--- a/pkg/locales/middleware/caching_middleware.go
+++ b/pkg/locales/middleware/caching_middleware.go
@@ -4,6 +4,7 @@ import (
 	"context"
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 	service "git.perx.ru/perxis/perxis-go/pkg/locales"
 )
 
@@ -25,29 +26,39 @@ func (m cachingMiddleware) Create(ctx context.Context, locale *service.Locale) (
 
 	loc, err = m.next.Create(ctx, locale)
 	if err == nil {
-		m.cache.Remove(loc.SpaceID)
+		_ = m.cache.Remove(loc.SpaceID)
 	}
 	return loc, err
 }
 
+func (m cachingMiddleware) Update(ctx context.Context, locale *service.Locale) (err error) {
+
+	err = m.next.Update(ctx, locale)
+	if err == nil {
+		_ = m.cache.Remove(locale.SpaceID)
+	}
+	return err
+}
+
 func (m cachingMiddleware) List(ctx context.Context, spaceId string) (locales []*service.Locale, err error) {
 
 	value, e := m.cache.Get(spaceId)
 	if e == nil {
-		return value.([]*service.Locale), err
+		return data.CloneSlice(value.([]*service.Locale)), nil
 	}
 	locales, err = m.next.List(ctx, spaceId)
 	if err == nil {
-		m.cache.Set(spaceId, locales)
+		_ = m.cache.Set(spaceId, locales)
+		return data.CloneSlice(locales), nil
 	}
-	return locales, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, localeId string) (err error) {
 
 	err = m.next.Delete(ctx, spaceId, localeId)
 	if err == nil {
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
diff --git a/pkg/locales/middleware/caching_middleware_test.go b/pkg/locales/middleware/caching_middleware_test.go
index 281aa49611a83e4a0a28f1064dbf5fe149dd29f3..25b542c840c228324f843267ae3968f96d0ddfe5 100644
--- a/pkg/locales/middleware/caching_middleware_test.go
+++ b/pkg/locales/middleware/caching_middleware_test.go
@@ -37,7 +37,8 @@ func TestLocalesCache(t *testing.T) {
 
 		vl2, err := svc.List(ctx, spaceID)
 		require.NoError(t, err)
-		assert.Same(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+		assert.Equal(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+		assert.NotSame(t, vl1[0], vl2[0])
 
 		loc.AssertExpectations(t)
 	})
@@ -55,7 +56,8 @@ func TestLocalesCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			loc.On("Delete", mock.Anything, spaceID, loc1).Return(nil).Once()
 
@@ -83,7 +85,8 @@ func TestLocalesCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			loc.On("Create", mock.Anything, mock.Anything).Return(&locales.Locale{ID: loc2, Name: "name2", SpaceID: spaceID}, nil).Once()
 
@@ -103,6 +106,39 @@ func TestLocalesCache(t *testing.T) {
 			loc.AssertExpectations(t)
 		})
 
+		t.Run("After Update", func(t *testing.T) {
+			loc := &locmocks.Locales{}
+
+			svc := CachingMiddleware(cache.NewMemoryCache(size, ttl))(loc)
+
+			loc.On("List", mock.Anything, spaceID).Return([]*locales.Locale{{ID: loc1, Name: "name1", SpaceID: spaceID}}, nil).Once()
+
+			vl1, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+
+			vl2, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
+
+			loc.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
+
+			err = svc.Update(ctx, &locales.Locale{ID: loc2, Name: "name2", SpaceID: spaceID})
+			require.NoError(t, err)
+
+			loc.On("List", mock.Anything, spaceID).
+				Return([]*locales.Locale{
+					{ID: loc1, Name: "name1", SpaceID: spaceID},
+					{ID: loc2, Name: "name2", SpaceID: spaceID},
+				}, nil).Once()
+
+			vl3, err := svc.List(ctx, spaceID)
+			require.NoError(t, err)
+			assert.Len(t, vl3, 2, "Ожидается что после обновления объекта данные будут удалены из кеша и получены из сервиса.")
+
+			loc.AssertExpectations(t)
+		})
+
 		t.Run("After TTL expired", func(t *testing.T) {
 			loc := &locmocks.Locales{}
 
@@ -115,7 +151,8 @@ func TestLocalesCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается что при повторном запросе объекты будут получены из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			time.Sleep(2 * ttl)
 			loc.On("List", mock.Anything, spaceID).Return([]*locales.Locale{{ID: loc1, Name: "name1", SpaceID: spaceID}}, nil).Once()
@@ -123,6 +160,8 @@ func TestLocalesCache(t *testing.T) {
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что элементы будут получены из кэша.")
+			assert.Equal(t, vl2[0], vl3[0])
+			assert.NotSame(t, vl2[0], vl3[0])
 
 			loc.AssertExpectations(t)
 		})
diff --git a/pkg/locales/middleware/error_logging_middleware.go b/pkg/locales/middleware/error_logging_middleware.go
index a00f23a29aa8b5330e1097c670e6cc0edf5b9983..0dd4ae2e6748c35fa7a0592c274a9dfeb86f5d7b 100644
--- a/pkg/locales/middleware/error_logging_middleware.go
+++ b/pkg/locales/middleware/error_logging_middleware.go
@@ -58,3 +58,13 @@ func (m *errorLoggingMiddleware) List(ctx context.Context, spaceId string) (loca
 	}()
 	return m.next.List(ctx, spaceId)
 }
+
+func (m *errorLoggingMiddleware) Update(ctx context.Context, locale *locales.Locale) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Update(ctx, locale)
+}
diff --git a/pkg/locales/middleware/logging_middleware.go b/pkg/locales/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..2b6bfea5281a89bf9050094fe2d631fed200c9a4
--- /dev/null
+++ b/pkg/locales/middleware/logging_middleware.go
@@ -0,0 +1,88 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   locales.Locales
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next locales.Locales) locales.Locales {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Locales")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, locale *locales.Locale) (created *locales.Locale, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(locales.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, locale)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(locale))
+		return
+	}
+
+	logger.Info("Locale created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+	return created, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, locale *locales.Locale) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(locales.EventUpdate),
+		logzap.Object(locale),
+	)
+
+	err = m.next.Update(ctx, locale)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Locale updated", logzap.Channels(logzap.Userlog))
+	return err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (locales []*locales.Locale, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	locales, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return locales, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId, localeId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(locales.EventDelete),
+		logzap.Object(id.NewLocaleId(spaceId, localeId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, localeId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Locale deleted", logzap.Channels(logzap.Userlog))
+	return err
+}
diff --git a/pkg/locales/middleware/middleware.go b/pkg/locales/middleware/middleware.go
index 2598e397afafb552c16be249cfcf1ed62149339b..28c61113902b15acf844d8a6251f0369159128a0 100644
--- a/pkg/locales/middleware/middleware.go
+++ b/pkg/locales/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s locales.Locales, logger *zap.Logger, log_access bool) locales.Loc
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/locales/middleware/recovering_middleware.go b/pkg/locales/middleware/recovering_middleware.go
index 3229e52394d207361d608744b5dcf73585142958..85f14bdbe6493c40c24c91bf539e33b9ca7e397b 100644
--- a/pkg/locales/middleware/recovering_middleware.go
+++ b/pkg/locales/middleware/recovering_middleware.go
@@ -65,3 +65,15 @@ func (m *recoveringMiddleware) List(ctx context.Context, spaceId string) (locale
 
 	return m.next.List(ctx, spaceId)
 }
+
+func (m *recoveringMiddleware) Update(ctx context.Context, locale *locales.Locale) (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.Update(ctx, locale)
+}
diff --git a/pkg/locales/middleware/telemetry_middleware.go b/pkg/locales/middleware/telemetry_middleware.go
index 4efc5293b78781aaa5d8cbf77f4da82c714a6137..98fe346ea000409be27bfd21fccc6723ed09851b 100644
--- a/pkg/locales/middleware/telemetry_middleware.go
+++ b/pkg/locales/middleware/telemetry_middleware.go
@@ -1,10 +1,10 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ..\..\..\assets\templates\middleware\telemetry
+// template: ../../../assets/templates/middleware/telemetry
 // gowrap: http://github.com/hexdigest/gowrap
 
 package middleware
 
-//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/locales -i Locales -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/locales -i Locales -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
 
 // source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
@@ -149,3 +149,36 @@ func (_d telemetryMiddleware) List(ctx context.Context, spaceId string) (locales
 	}()
 	return _d.Locales.List(ctx, spaceId)
 }
+
+// Update implements locales.Locales
+func (_d telemetryMiddleware) Update(ctx context.Context, locale *locales.Locale) (err error) {
+	attributes := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Locales"),
+		attribute.String("method", "Update"),
+	))
+
+	_d.requestMetrics.Total.Add(ctx, 1, attributes)
+
+	start := time.Now()
+	ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Locales.Update")
+
+	defer func() {
+		_d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes)
+
+		if _d._spanDecorator != nil {
+			_d._spanDecorator(_span, map[string]interface{}{
+				"ctx":    ctx,
+				"locale": locale}, map[string]interface{}{
+				"err": err})
+		} else if err != nil {
+			_d.requestMetrics.FailedTotal.Add(ctx, 1, attributes)
+
+			_span.RecordError(err)
+			_span.SetAttributes(attribute.String("event", "error"))
+			_span.SetAttributes(attribute.String("message", err.Error()))
+		}
+
+		_span.End()
+	}()
+	return _d.Locales.Update(ctx, locale)
+}
diff --git a/pkg/locales/mocks/Locales.go b/pkg/locales/mocks/Locales.go
index 491406c5c3fa577050ee0f1a097b89419b17bf42..6519e3cc9b5e02895fa22b778c15437737f606de 100644
--- a/pkg/locales/mocks/Locales.go
+++ b/pkg/locales/mocks/Locales.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -18,7 +18,15 @@ type Locales struct {
 func (_m *Locales) Create(ctx context.Context, locale *locales.Locale) (*locales.Locale, error) {
 	ret := _m.Called(ctx, locale)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Create")
+	}
+
 	var r0 *locales.Locale
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) (*locales.Locale, error)); ok {
+		return rf(ctx, locale)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) *locales.Locale); ok {
 		r0 = rf(ctx, locale)
 	} else {
@@ -27,7 +35,6 @@ func (_m *Locales) Create(ctx context.Context, locale *locales.Locale) (*locales
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *locales.Locale) error); ok {
 		r1 = rf(ctx, locale)
 	} else {
@@ -41,6 +48,10 @@ func (_m *Locales) Create(ctx context.Context, locale *locales.Locale) (*locales
 func (_m *Locales) Delete(ctx context.Context, spaceId string, localeId string) error {
 	ret := _m.Called(ctx, spaceId, localeId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Delete")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
 		r0 = rf(ctx, spaceId, localeId)
@@ -55,7 +66,15 @@ func (_m *Locales) Delete(ctx context.Context, spaceId string, localeId string)
 func (_m *Locales) List(ctx context.Context, spaceId string) ([]*locales.Locale, error) {
 	ret := _m.Called(ctx, spaceId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for List")
+	}
+
 	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 {
@@ -64,7 +83,6 @@ func (_m *Locales) List(ctx context.Context, spaceId string) ([]*locales.Locale,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -74,13 +92,30 @@ func (_m *Locales) List(ctx context.Context, spaceId string) ([]*locales.Locale,
 	return r0, r1
 }
 
-type mockConstructorTestingTNewLocales interface {
-	mock.TestingT
-	Cleanup(func())
+// Update provides a mock function with given fields: ctx, locale
+func (_m *Locales) Update(ctx context.Context, locale *locales.Locale) error {
+	ret := _m.Called(ctx, locale)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Update")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) error); ok {
+		r0 = rf(ctx, locale)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
 }
 
 // NewLocales creates a new instance of Locales. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewLocales(t mockConstructorTestingTNewLocales) *Locales {
+// The first argument is typically a *testing.T value.
+func NewLocales(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *Locales {
 	mock := &Locales{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/locales/mocks/Storage.go b/pkg/locales/mocks/Storage.go
index d37bd81cf7011e2498768c76102bb71a689eaf84..42946781b45c100bfb962fde0a2f6098699cec80 100644
--- a/pkg/locales/mocks/Storage.go
+++ b/pkg/locales/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.43.2. DO NOT EDIT.
 
 package mocks
 
@@ -20,7 +20,15 @@ type Storage struct {
 func (_m *Storage) Create(ctx context.Context, locale *locales.Locale) (*locales.Locale, error) {
 	ret := _m.Called(ctx, locale)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Create")
+	}
+
 	var r0 *locales.Locale
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) (*locales.Locale, error)); ok {
+		return rf(ctx, locale)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) *locales.Locale); ok {
 		r0 = rf(ctx, locale)
 	} else {
@@ -29,7 +37,6 @@ func (_m *Storage) Create(ctx context.Context, locale *locales.Locale) (*locales
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *locales.Locale) error); ok {
 		r1 = rf(ctx, locale)
 	} else {
@@ -43,14 +50,21 @@ func (_m *Storage) Create(ctx context.Context, locale *locales.Locale) (*locales
 func (_m *Storage) Delete(ctx context.Context, spaceID string, filter *locales.Filter) (int, error) {
 	ret := _m.Called(ctx, spaceID, filter)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Delete")
+	}
+
 	var r0 int
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, *locales.Filter) (int, error)); ok {
+		return rf(ctx, spaceID, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, *locales.Filter) int); ok {
 		r0 = rf(ctx, spaceID, filter)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, *locales.Filter) error); ok {
 		r1 = rf(ctx, spaceID, filter)
 	} else {
@@ -64,7 +78,16 @@ func (_m *Storage) Delete(ctx context.Context, spaceID string, filter *locales.F
 func (_m *Storage) Find(ctx context.Context, spaceID string, filter *locales.Filter, opts *options.FindOptions) ([]*locales.Locale, int, error) {
 	ret := _m.Called(ctx, spaceID, filter, opts)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Find")
+	}
+
 	var r0 []*locales.Locale
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, *locales.Filter, *options.FindOptions) ([]*locales.Locale, int, error)); ok {
+		return rf(ctx, spaceID, filter, opts)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, *locales.Filter, *options.FindOptions) []*locales.Locale); ok {
 		r0 = rf(ctx, spaceID, filter, opts)
 	} else {
@@ -73,14 +96,12 @@ func (_m *Storage) Find(ctx context.Context, spaceID string, filter *locales.Fil
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, string, *locales.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, *locales.Filter, *options.FindOptions) error); ok {
 		r2 = rf(ctx, spaceID, filter, opts)
 	} else {
@@ -94,6 +115,10 @@ func (_m *Storage) Find(ctx context.Context, spaceID string, filter *locales.Fil
 func (_m *Storage) Reset(ctx context.Context, spaceID string) error {
 	ret := _m.Called(ctx, spaceID)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Reset")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
 		r0 = rf(ctx, spaceID)
@@ -104,13 +129,30 @@ func (_m *Storage) Reset(ctx context.Context, spaceID string) error {
 	return r0
 }
 
-type mockConstructorTestingTNewStorage interface {
-	mock.TestingT
-	Cleanup(func())
+// Update provides a mock function with given fields: ctx, locale
+func (_m *Storage) Update(ctx context.Context, locale *locales.Locale) error {
+	ret := _m.Called(ctx, locale)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Update")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.Locale) error); ok {
+		r0 = rf(ctx, locale)
+	} 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.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/locales/observer.go b/pkg/locales/observer.go
new file mode 100644
index 0000000000000000000000000000000000000000..b6f7a56f6ba8971c7ce56c49ed77b592a0afd38c
--- /dev/null
+++ b/pkg/locales/observer.go
@@ -0,0 +1,15 @@
+package locales
+
+import "context"
+
+type LocaleCreatedObserver interface {
+	OnLocaleCreated(ctx context.Context, locale *Locale) (delayedTaskID string, err error)
+}
+
+type LocaleUpdatedObserver interface {
+	OnLocaleUpdated(ctx context.Context, before, locale *Locale) (delayedTaskID string, err error)
+}
+
+type LocaleDeletedObserver interface {
+	OnLocaleDeleted(ctx context.Context, locale *Locale) (delayedTaskID string, err error)
+}
diff --git a/pkg/locales/service.go b/pkg/locales/service.go
index 7724d7f7ecdb2f4733d97bff01113d105adabdeb..ba303e13f671a700a5518ca1b200cb48dcad5695 100644
--- a/pkg/locales/service.go
+++ b/pkg/locales/service.go
@@ -9,6 +9,7 @@ import (
 // @grpc-addr content.locales.Locales
 type Locales interface {
 	Create(ctx context.Context, locale *Locale) (created *Locale, err error)
+	Update(ctx context.Context, locale *Locale) (err error)
 	List(ctx context.Context, spaceId string) (locales []*Locale, err error)
 	Delete(ctx context.Context, spaceId, localeId string) (err error)
 }
diff --git a/pkg/locales/storage.go b/pkg/locales/storage.go
index c345706c5925119cf3eb84a07f91e57ff764f46f..facbb380e33d5f5e92e778cb54873b4d5fca4746 100644
--- a/pkg/locales/storage.go
+++ b/pkg/locales/storage.go
@@ -10,11 +10,18 @@ type Storage interface {
 	Reset(ctx context.Context, spaceID string) error
 
 	Create(ctx context.Context, locale *Locale) (created *Locale, err error)
+	Update(ctx context.Context, locale *Locale) (err error)
 	Find(ctx context.Context, spaceID string, filter *Filter, opts *options.FindOptions) (locales []*Locale, total int, err error)
 	Delete(ctx context.Context, spaceID string, filter *Filter) (total int, err error)
 }
 
 type Filter struct {
-	ID   []string
-	Name []string
+	ID         []string `json:"id"`
+	Name       []string `json:"name"`
+	NativeName []string `json:"native_name"`
+	Code       []string `json:"code"`
+	Fallback   []string `json:"fallback"`
+	Direction  []string `json:"direction"`
+	NoPublish  *bool    `json:"no_publish"`
+	Disabled   *bool    `json:"disabled"`
 }
diff --git a/pkg/locales/transport/client.microgen.go b/pkg/locales/transport/client.go
similarity index 62%
rename from pkg/locales/transport/client.microgen.go
rename to pkg/locales/transport/client.go
index f8cd9dee23dafeff1d635b3f5ee42f6e5c8667f1..1aa9dc5169737c91ef96c981405ec6dbcea048c2 100644
--- a/pkg/locales/transport/client.microgen.go
+++ b/pkg/locales/transport/client.go
@@ -4,32 +4,32 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	locales "git.perx.ru/perxis/perxis-go/pkg/locales"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *locales.Locale) (res0 *locales.Locale, res1 error) {
 	request := CreateRequest{Locale: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
 }
 
+func (set EndpointsSet) Update(arg0 context.Context, arg1 *locales.Locale) (res1 error) {
+	request := UpdateRequest{Locale: arg1}
+	_, res1 = set.UpdateEndpoint(arg0, &request)
+	if res1 != nil {
+		return
+	}
+	return res1
+}
+
 func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*locales.Locale, res1 error) {
 	request := ListRequest{SpaceId: arg1}
 	response, res1 := set.ListEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListResponse).Locales, res1
@@ -37,14 +37,11 @@ func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*locales
 
 func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (res0 error) {
 	request := DeleteRequest{
-		LocaleId: arg2,
-		SpaceId:  arg1,
+		Id:      arg2,
+		SpaceId: arg1,
 	}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/locales/transport/endpoints.microgen.go b/pkg/locales/transport/endpoints.microgen.go
index ffca7318747104f8b58af332e90646f8cc6a8b9c..44dca7f2185e630db610b4ec162cfa125de97d49 100644
--- a/pkg/locales/transport/endpoints.microgen.go
+++ b/pkg/locales/transport/endpoints.microgen.go
@@ -7,6 +7,7 @@ import endpoint "github.com/go-kit/kit/endpoint"
 // EndpointsSet implements Locales API and used for transport purposes.
 type EndpointsSet struct {
 	CreateEndpoint endpoint.Endpoint
+	UpdateEndpoint endpoint.Endpoint
 	ListEndpoint   endpoint.Endpoint
 	DeleteEndpoint endpoint.Endpoint
 }
diff --git a/pkg/locales/transport/exchanges.microgen.go b/pkg/locales/transport/exchanges.microgen.go
index a07204e13a233fd7bae40c74e9984061871a92b7..14a4f1a6492f172de8f2b3df62ae6f632804f62f 100644
--- a/pkg/locales/transport/exchanges.microgen.go
+++ b/pkg/locales/transport/exchanges.microgen.go
@@ -12,6 +12,12 @@ type (
 		Created *locales.Locale `json:"created"`
 	}
 
+	UpdateRequest struct {
+		Locale *locales.Locale `json:"locale"`
+	}
+	// Formal exchange type, please do not delete.
+	UpdateResponse struct{}
+
 	ListRequest struct {
 		SpaceId string `json:"space_id"`
 	}
@@ -20,8 +26,8 @@ type (
 	}
 
 	DeleteRequest struct {
-		SpaceId  string `json:"space_id"`
-		LocaleId string `json:"locale_id"`
+		Id      string `json:"id"`
+		SpaceId string `json:"space_id"`
 	}
 	// Formal exchange type, please do not delete.
 	DeleteResponse struct{}
diff --git a/pkg/locales/transport/grpc/client.go b/pkg/locales/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..877400ddeeaa54b265e43407bff4a9df548ff1be
--- /dev/null
+++ b/pkg/locales/transport/grpc/client.go
@@ -0,0 +1,20 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/locales/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ClientMiddleware(c.CreateEndpoint),
+		UpdateEndpoint: grpcerr.ClientMiddleware(c.UpdateEndpoint),
+		DeleteEndpoint: grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		ListEndpoint:   grpcerr.ClientMiddleware(c.ListEndpoint),
+	}
+}
diff --git a/pkg/locales/transport/grpc/client.microgen.go b/pkg/locales/transport/grpc/client.microgen.go
index 214cdf463436c71195457305de39516bd24e20d0..a8c0b9fcb06a8ed08edf72e495ceb240e2e53b80 100644
--- a/pkg/locales/transport/grpc/client.microgen.go
+++ b/pkg/locales/transport/grpc/client.microgen.go
@@ -22,6 +22,13 @@ func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOpt
 			pb.CreateResponse{},
 			opts...,
 		).Endpoint(),
+		UpdateEndpoint: grpckit.NewClient(
+			conn, addr, "Update",
+			_Encode_Update_Request,
+			_Decode_Update_Response,
+			empty.Empty{},
+			opts...,
+		).Endpoint(),
 		DeleteEndpoint: grpckit.NewClient(
 			conn, addr, "Delete",
 			_Encode_Delete_Request,
diff --git a/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go
index f8124ba307afb6caa42b881a0c5778f465bf6f0f..c9c534131bf7e3be03bce9aeadfa3957d252ae2f 100644
--- a/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go
+++ b/pkg/locales/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -24,6 +24,18 @@ func _Encode_Create_Request(ctx context.Context, request interface{}) (interface
 	return &pb.CreateRequest{Locale: pbLocale}, nil
 }
 
+func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*transport.UpdateRequest)
+	pbLocale, err := PtrLocaleToProto(req.Locale)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.UpdateRequest{Locale: pbLocale}, nil
+}
+
 func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
 	if request == nil {
 		return nil, errors.New("nil ListRequest")
@@ -38,8 +50,8 @@ func _Encode_Delete_Request(ctx context.Context, request interface{}) (interface
 	}
 	req := request.(*transport.DeleteRequest)
 	return &pb.DeleteRequest{
-		LocaleId: req.LocaleId,
-		SpaceId:  req.SpaceId,
+		Id:      req.Id,
+		SpaceId: req.SpaceId,
 	}, nil
 }
 
@@ -55,6 +67,10 @@ func _Encode_Create_Response(ctx context.Context, response interface{}) (interfa
 	return &pb.CreateResponse{Locale: respLocale}, nil
 }
 
+func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
 func _Encode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
 	if response == nil {
 		return nil, errors.New("nil ListResponse")
@@ -83,6 +99,18 @@ func _Decode_Create_Request(ctx context.Context, request interface{}) (interface
 	return &transport.CreateRequest{Locale: locale}, nil
 }
 
+func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil UpdateRequest")
+	}
+	req := request.(*pb.UpdateRequest)
+	locale, err := ProtoToPtrLocale(req.Locale)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.UpdateRequest{Locale: locale}, nil
+}
+
 func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}, error) {
 	if request == nil {
 		return nil, errors.New("nil ListRequest")
@@ -97,8 +125,8 @@ func _Decode_Delete_Request(ctx context.Context, request interface{}) (interface
 	}
 	req := request.(*pb.DeleteRequest)
 	return &transport.DeleteRequest{
-		LocaleId: string(req.LocaleId),
-		SpaceId:  string(req.SpaceId),
+		Id:      string(req.Id),
+		SpaceId: string(req.SpaceId),
 	}, nil
 }
 
@@ -114,6 +142,10 @@ func _Decode_Create_Response(ctx context.Context, response interface{}) (interfa
 	return &transport.CreateResponse{Created: respLocale}, nil
 }
 
+func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
 func _Decode_List_Response(ctx context.Context, response interface{}) (interface{}, error) {
 	if response == nil {
 		return nil, errors.New("nil ListResponse")
diff --git a/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go b/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go
index 6dca0bb8afa68dfe3fa17b53d315f3716c48ae36..4932a7d497ea2d0df148dddbfb0a7bbda3dca300 100644
--- a/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/locales/transport/grpc/protobuf_type_converters.microgen.go
@@ -13,14 +13,14 @@ func PtrLocaleToProto(locale *service.Locale) (*pb.Locale, error) {
 	if locale == nil {
 		return nil, nil
 	}
-	return &pb.Locale{Id: locale.ID, Name: locale.Name, SpaceId: locale.SpaceID}, nil
+	return service.LocaleToProto(locale), nil
 }
 
 func ProtoToPtrLocale(protoLocale *pb.Locale) (*service.Locale, error) {
 	if protoLocale == nil {
 		return nil, nil
 	}
-	return &service.Locale{ID: protoLocale.Id, Name: protoLocale.Name, SpaceID: protoLocale.SpaceId}, nil
+	return service.LocaleFromProto(protoLocale), nil
 }
 
 func ListPtrLocaleToProto(locales []*service.Locale) ([]*pb.Locale, error) {
diff --git a/pkg/locales/transport/grpc/server.go b/pkg/locales/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..ca4f95aeff638c95da871d40b9ce999b1b108299
--- /dev/null
+++ b/pkg/locales/transport/grpc/server.go
@@ -0,0 +1,20 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"git.perx.ru/perxis/perxis-go/pkg/locales/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/locales"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc locales.Locales, opts ...grpckit.ServerOption) pb.LocalesServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		UpdateEndpoint: grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+		DeleteEndpoint: grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		ListEndpoint:   grpcerr.ServerMiddleware(eps.ListEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/locales/transport/grpc/server.microgen.go b/pkg/locales/transport/grpc/server.microgen.go
index fe25ca1a7b676599d5d09463cdbb6c11aba87939..80778f883bafb2aba5d926526b162cded6db53a7 100644
--- a/pkg/locales/transport/grpc/server.microgen.go
+++ b/pkg/locales/transport/grpc/server.microgen.go
@@ -13,6 +13,7 @@ import (
 
 type localesServer struct {
 	create grpc.Handler
+	update grpc.Handler
 	list   grpc.Handler
 	delete grpc.Handler
 
@@ -27,6 +28,12 @@ func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption)
 			_Encode_Create_Response,
 			opts...,
 		),
+		update: grpc.NewServer(
+			endpoints.UpdateEndpoint,
+			_Decode_Update_Request,
+			_Encode_Update_Response,
+			opts...,
+		),
 		delete: grpc.NewServer(
 			endpoints.DeleteEndpoint,
 			_Decode_Delete_Request,
@@ -50,6 +57,14 @@ func (S *localesServer) Create(ctx context.Context, req *pb.CreateRequest) (*pb.
 	return resp.(*pb.CreateResponse), nil
 }
 
+func (S *localesServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) {
+	_, resp, err := S.update.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*empty.Empty), nil
+}
+
 func (S *localesServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListResponse, error) {
 	_, resp, err := S.list.ServeGRPC(ctx, req)
 	if err != nil {
diff --git a/pkg/locales/transport/server.microgen.go b/pkg/locales/transport/server.microgen.go
index 5ce815dcb52415a93ddfeb095f99e22fb14c4492..081edcd5c304fa5526e031181e80c78903b1e707 100644
--- a/pkg/locales/transport/server.microgen.go
+++ b/pkg/locales/transport/server.microgen.go
@@ -12,6 +12,7 @@ import (
 func Endpoints(svc locales.Locales) EndpointsSet {
 	return EndpointsSet{
 		CreateEndpoint: CreateEndpoint(svc),
+		UpdateEndpoint: UpdateEndpoint(svc),
 		DeleteEndpoint: DeleteEndpoint(svc),
 		ListEndpoint:   ListEndpoint(svc),
 	}
@@ -25,6 +26,14 @@ func CreateEndpoint(svc locales.Locales) endpoint.Endpoint {
 	}
 }
 
+func UpdateEndpoint(svc locales.Locales) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*UpdateRequest)
+		res1 := svc.Update(arg0, req.Locale)
+		return &UpdateResponse{}, res1
+	}
+}
+
 func ListEndpoint(svc locales.Locales) endpoint.Endpoint {
 	return func(arg0 context.Context, request interface{}) (interface{}, error) {
 		req := request.(*ListRequest)
@@ -36,7 +45,7 @@ func ListEndpoint(svc locales.Locales) endpoint.Endpoint {
 func DeleteEndpoint(svc locales.Locales) endpoint.Endpoint {
 	return func(arg0 context.Context, request interface{}) (interface{}, error) {
 		req := request.(*DeleteRequest)
-		res0 := svc.Delete(arg0, req.SpaceId, req.LocaleId)
+		res0 := svc.Delete(arg0, req.SpaceId, req.Id)
 		return &DeleteResponse{}, res0
 	}
 }
diff --git a/pkg/members/events.go b/pkg/members/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..5fef6d772c510e5adf37bedc9738c6c50e7a9ee4
--- /dev/null
+++ b/pkg/members/events.go
@@ -0,0 +1,7 @@
+package members
+
+const (
+	EventSet       = "members.set"
+	EventRemove    = "members.remove"
+	EventRemoveAll = "members.remove_all"
+)
diff --git a/pkg/members/members.go b/pkg/members/members.go
index 0993b6fddfe34cf7f257e6b70433893485a60dba..28ae060e2dacf12a61ef3e9d99831e01689d39b2 100644
--- a/pkg/members/members.go
+++ b/pkg/members/members.go
@@ -2,6 +2,7 @@ package members
 
 import (
 	"fmt"
+	"strconv"
 )
 
 type Member struct {
@@ -10,6 +11,14 @@ type Member struct {
 	Role   Role   `bson:"role"`
 }
 
+func (m Member) Clone() *Member {
+	return &Member{
+		OrgId:  m.OrgId,
+		UserId: m.UserId,
+		Role:   m.Role,
+	}
+}
+
 type Role uint
 
 const (
@@ -24,12 +33,37 @@ func (r Role) IsPrivileged() bool {
 }
 
 func (r Role) Format(s fmt.State, verb rune) {
+	switch verb {
+	case 's':
+		_, _ = s.Write([]byte(r.String()))
+	default:
+		_, _ = s.Write([]byte(strconv.Itoa(int(r))))
+	}
+}
+
+func (r Role) String() string {
 	switch r {
+	case NotMember:
+		return "not member"
+	case RoleMember:
+		return "member"
 	case RoleOwner:
-		fmt.Fprint(s, "owner")
+		return "owner"
 	case RoleAdmin:
-		fmt.Fprint(s, "admin")
-	case RoleMember:
-		fmt.Fprint(s, "member")
+		return "admin"
+	}
+	return fmt.Sprintf("%d", r)
+}
+
+func RoleFromString(r string) Role {
+	switch r {
+	case RoleMember.String():
+		return RoleMember
+	case RoleOwner.String():
+		return RoleOwner
+	case RoleAdmin.String():
+		return RoleAdmin
+	default:
+		return NotMember
 	}
 }
diff --git a/pkg/members/members_test.go b/pkg/members/members_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4343b75e17b4d91743f1faf3e69bdce81851e03c
--- /dev/null
+++ b/pkg/members/members_test.go
@@ -0,0 +1,65 @@
+package members
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestFormat(t *testing.T) {
+	tests := []struct {
+		name     string
+		template string
+		want     string
+		role     Role
+	}{
+		{
+			name:     "NotMember with %s",
+			template: "%s",
+			role:     NotMember,
+			want:     "not member",
+		},
+		{
+			name:     "RoleMember with %s",
+			role:     RoleMember,
+			template: "%s",
+			want:     "member",
+		},
+		{
+			name:     "RoleOwner with %s",
+			role:     RoleOwner,
+			template: "%s",
+			want:     "owner",
+		},
+		{
+			name:     "RoleAdmin with %s",
+			role:     RoleAdmin,
+			template: "%s",
+			want:     "admin",
+		},
+		{
+			name:     "Existent role with %d",
+			role:     RoleAdmin,
+			template: "%d",
+			want:     "3",
+		},
+		{
+			name:     "Non-existent role with %s",
+			role:     4,
+			template: "%s",
+			want:     "4",
+		},
+		{
+			name:     "Non-existent role with %d",
+			role:     4,
+			template: "%d",
+			want:     "4",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			require.Equal(t, tt.want, fmt.Sprintf(tt.template, tt.role))
+		})
+	}
+}
diff --git a/pkg/members/middleware/caching_middleware.go b/pkg/members/middleware/caching_middleware.go
index 0af3f76f9e5aa1eeeee01c36f2e7509e35e951c3..4c61bff9078028009130bc7378908a6db551bba2 100644
--- a/pkg/members/middleware/caching_middleware.go
+++ b/pkg/members/middleware/caching_middleware.go
@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 	service "git.perx.ru/perxis/perxis-go/pkg/members"
 )
 
@@ -30,9 +31,9 @@ func (m cachingMiddleware) Set(ctx context.Context, orgId string, userId string,
 
 	err = m.next.Set(ctx, orgId, userId, role)
 	if err == nil {
-		m.cache.Remove(makeKey(orgId, userId))
-		m.cache.Remove(makeKey(orgId))
-		m.cache.Remove(makeKey(userId))
+		_ = m.cache.Remove(makeKey(orgId, userId))
+		_ = m.cache.Remove(makeKey(orgId))
+		_ = m.cache.Remove(makeKey(userId))
 	}
 	return err
 }
@@ -46,7 +47,7 @@ func (m cachingMiddleware) Get(ctx context.Context, orgId string, userId string)
 	}
 	role, err = m.next.Get(ctx, orgId, userId)
 	if err == nil {
-		m.cache.Set(key, role)
+		_ = m.cache.Set(key, role)
 	}
 	return role, err
 }
@@ -55,9 +56,9 @@ func (m cachingMiddleware) Remove(ctx context.Context, orgId string, userId stri
 
 	err = m.next.Remove(ctx, orgId, userId)
 	if err == nil {
-		m.cache.Remove(makeKey(orgId, userId))
-		m.cache.Remove(makeKey(orgId))
-		m.cache.Remove(makeKey(userId))
+		_ = m.cache.Remove(makeKey(orgId, userId))
+		_ = m.cache.Remove(makeKey(orgId))
+		_ = m.cache.Remove(makeKey(userId))
 	}
 	return err
 }
@@ -68,8 +69,8 @@ func (m cachingMiddleware) RemoveAll(ctx context.Context, orgId string) (err err
 	if err == nil {
 		members, _ := m.ListMembers(ctx, orgId)
 		for _, member := range members {
-			m.cache.Remove(member.UserId)
-			m.cache.Remove(makeKey(orgId, member.UserId))
+			_ = m.cache.Remove(member.UserId)
+			_ = m.cache.Remove(makeKey(orgId, member.UserId))
 		}
 	}
 	return err
@@ -79,24 +80,26 @@ func (m cachingMiddleware) ListMembers(ctx context.Context, orgId string) (membe
 
 	value, e := m.cache.Get(makeKey(orgId))
 	if e == nil {
-		return value.([]*service.Member), err
+		return data.CloneSlice(value.([]*service.Member)), nil
 	}
 	members, err = m.next.ListMembers(ctx, orgId)
 	if err == nil {
-		m.cache.Set(makeKey(orgId), members)
+		_ = m.cache.Set(makeKey(orgId), members)
+		return data.CloneSlice(members), nil
 	}
-	return members, err
+	return nil, err
 }
 
 func (m cachingMiddleware) ListOrganizations(ctx context.Context, userId string) (members []*service.Member, err error) {
 
 	value, e := m.cache.Get(makeKey(userId))
 	if e == nil {
-		return value.([]*service.Member), err
+		return data.CloneSlice(value.([]*service.Member)), nil
 	}
 	members, err = m.next.ListOrganizations(ctx, userId)
 	if err == nil {
-		m.cache.Set(makeKey(userId), members)
+		_ = m.cache.Set(makeKey(userId), members)
+		return data.CloneSlice(members), nil
 	}
-	return members, err
+	return nil, err
 }
diff --git a/pkg/members/middleware/caching_middleware_test.go b/pkg/members/middleware/caching_middleware_test.go
index eda7eb3a2a85123d36d20f09e9bd0c5188b609a9..ea09d522188857ee841336e4d8dbdf9cad7ff55c 100644
--- a/pkg/members/middleware/caching_middleware_test.go
+++ b/pkg/members/middleware/caching_middleware_test.go
@@ -129,6 +129,7 @@ func TestMembersCache(t *testing.T) {
 			mbrs.On("Get", mock.Anything, orgId, userId).Return(members.RoleOwner, nil).Once()
 
 			v1, err := svc.Get(ctx, orgId, userId)
+			require.NoError(t, err)
 
 			v2, err := svc.Get(ctx, orgId, userId)
 			require.NoError(t, err)
diff --git a/pkg/members/middleware/logging_middleware.go b/pkg/members/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..b967a8b4ffb5729baf69c6d08899fdb7dfa0f0ea
--- /dev/null
+++ b/pkg/members/middleware/logging_middleware.go
@@ -0,0 +1,122 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/members"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   members.Members
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next members.Members) members.Members {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Members")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Set(ctx context.Context, orgId, userId string, role members.Role) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(members.EventSet),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.Set(ctx, orgId, userId, role)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to assign user '%s' to role '%s'", userId, role), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' assigned to role '%s'", userId, role), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, orgId, userId string) (role members.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewRoleId(orgId, userId)),
+	)
+
+	role, err = m.next.Get(ctx, orgId, userId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return role, err
+}
+
+func (m *loggingMiddleware) Remove(ctx context.Context, orgId, userId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(members.EventRemove),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.Remove(ctx, orgId, userId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to remove user '%s'", userId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("User '%s' removed", userId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) RemoveAll(ctx context.Context, orgId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(members.EventRemoveAll),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.RemoveAll(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to remove all members", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("All members removed", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) ListMembers(ctx context.Context, orgId string) (members []*members.Member, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	members, err = m.next.ListMembers(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to list members", zap.Error(err))
+		return
+	}
+
+	return members, err
+}
+
+func (m *loggingMiddleware) ListOrganizations(ctx context.Context, userId string) (organizations []*members.Member, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	organizations, err = m.next.ListOrganizations(ctx, userId)
+	if err != nil {
+		logger.Error("Failed to list organizations", zap.Error(err))
+		return
+	}
+
+	return organizations, err
+}
diff --git a/pkg/members/middleware/middleware.go b/pkg/members/middleware/middleware.go
index bb491623865e21496adedb1e91987bc2205a3ce4..04626790010ac2da99f00e8501d1c4186683e6b7 100644
--- a/pkg/members/middleware/middleware.go
+++ b/pkg/members/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s members.Members, logger *zap.Logger, log_access bool) members.Mem
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/members/observer/transport/client.microgen.go b/pkg/members/observer/transport/client.go
similarity index 68%
rename from pkg/members/observer/transport/client.microgen.go
rename to pkg/members/observer/transport/client.go
index 28ca2a88bdbb5e5095b8c2f7d2e9a5e85b67cc09..957fd556fe9e43bac595faed40a5da3b6bd06045 100644
--- a/pkg/members/observer/transport/client.microgen.go
+++ b/pkg/members/observer/transport/client.go
@@ -4,20 +4,14 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	collaborators "git.perx.ru/perxis/perxis-go/pkg/collaborators"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) OnCollaboratorSet(arg0 context.Context, arg1 *collaborators.Collaborator) (res0 string, res1 error) {
 	request := OnCollaboratorSetRequest{Collaborator: arg1}
 	response, res1 := set.OnCollaboratorSetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*OnCollaboratorSetResponse).DelayedTaskID, res1
diff --git a/pkg/members/observer/transport/grpc/client.go b/pkg/members/observer/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..321ee07dcfe5a964c60a114c18b8f2abe88cc2a3
--- /dev/null
+++ b/pkg/members/observer/transport/grpc/client.go
@@ -0,0 +1,17 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/members/observer/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		OnCollaboratorSetEndpoint: grpcerr.ClientMiddleware(c.OnCollaboratorSetEndpoint),
+	}
+}
diff --git a/pkg/members/observer/transport/grpc/server.go b/pkg/members/observer/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..beb4b614b2466ea6bbe9aa7da73b64c863fad276
--- /dev/null
+++ b/pkg/members/observer/transport/grpc/server.go
@@ -0,0 +1,17 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/members/observer"
+	"git.perx.ru/perxis/perxis-go/pkg/members/observer/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/members"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc observer.Observer, opts ...grpckit.ServerOption) pb.ObserverServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		OnCollaboratorSetEndpoint: grpcerr.ServerMiddleware(eps.OnCollaboratorSetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/members/transport/client.microgen.go b/pkg/members/transport/client.go
similarity index 68%
rename from pkg/members/transport/client.microgen.go
rename to pkg/members/transport/client.go
index 4cd10cad5b1b970f30a9380eae77a5d706748a08..8b3b27bdf0eb100e2b147505d3a638812ee3286b 100644
--- a/pkg/members/transport/client.microgen.go
+++ b/pkg/members/transport/client.go
@@ -4,11 +4,8 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	members "git.perx.ru/perxis/perxis-go/pkg/members"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Set(arg0 context.Context, arg1 string, arg2 string, arg3 members.Role) (res0 error) {
@@ -19,9 +16,6 @@ func (set EndpointsSet) Set(arg0 context.Context, arg1 string, arg2 string, arg3
 	}
 	_, res0 = set.SetEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -34,9 +28,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Role, res1
@@ -49,9 +40,6 @@ func (set EndpointsSet) Remove(arg0 context.Context, arg1 string, arg2 string) (
 	}
 	_, res0 = set.RemoveEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -65,9 +53,6 @@ func (set EndpointsSet) ListMembers(arg0 context.Context, arg1 string) (res0 []*
 	request := ListMembersRequest{OrgId: arg1}
 	response, res1 := set.ListMembersEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListMembersResponse).Members, res1
@@ -77,9 +62,6 @@ func (set EndpointsSet) ListOrganizations(arg0 context.Context, arg1 string) (re
 	request := ListOrganizationsRequest{UserId: arg1}
 	response, res1 := set.ListOrganizationsEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListOrganizationsResponse).Organizations, res1
diff --git a/pkg/members/transport/grpc/client.go b/pkg/members/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc79e343c6bb173a64752d536bdd6bc1ba88e740
--- /dev/null
+++ b/pkg/members/transport/grpc/client.go
@@ -0,0 +1,21 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/members/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		GetEndpoint:               grpcerr.ClientMiddleware(c.GetEndpoint),
+		ListMembersEndpoint:       grpcerr.ClientMiddleware(c.ListMembersEndpoint),
+		ListOrganizationsEndpoint: grpcerr.ClientMiddleware(c.ListOrganizationsEndpoint),
+		RemoveEndpoint:            grpcerr.ClientMiddleware(c.RemoveEndpoint),
+		SetEndpoint:               grpcerr.ClientMiddleware(c.SetEndpoint),
+	}
+}
diff --git a/pkg/members/transport/grpc/server.go b/pkg/members/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..d87048167b71a4cc22e031f164ee4725e7534428
--- /dev/null
+++ b/pkg/members/transport/grpc/server.go
@@ -0,0 +1,21 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/members"
+	"git.perx.ru/perxis/perxis-go/pkg/members/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/members"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc members.Members, opts ...grpckit.ServerOption) pb.MembersServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		GetEndpoint:               grpcerr.ServerMiddleware(eps.GetEndpoint),
+		ListMembersEndpoint:       grpcerr.ServerMiddleware(eps.ListMembersEndpoint),
+		ListOrganizationsEndpoint: grpcerr.ServerMiddleware(eps.ListOrganizationsEndpoint),
+		RemoveEndpoint:            grpcerr.ServerMiddleware(eps.RemoveEndpoint),
+		SetEndpoint:               grpcerr.ServerMiddleware(eps.SetEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/operation/operation.go b/pkg/operation/operation.go
index bbe73370c21ce1a0c290fd53cd8a2225ba62cc4a..c6c46ca3a4353bbc663fb553477c0e8adcfd849f 100644
--- a/pkg/operation/operation.go
+++ b/pkg/operation/operation.go
@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"time"
 
+	"git.perx.ru/perxis/perxis-go/id"
 	"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"
diff --git a/pkg/operation/service.go b/pkg/operation/service.go
index b4d5c30e757771bae22b7b460fd2c7e5336a80fc..06512505fdb50569a99fc0eeb79193bb0d62be87 100644
--- a/pkg/operation/service.go
+++ b/pkg/operation/service.go
@@ -129,7 +129,7 @@ func (s *service) Create(ctx context.Context, desc string, fn func(ctx context.C
 	// Создаем новый контекст для фоновой операции без Deadline
 	opCtx, cancel := context.WithCancel(auth.WithPrincipal(context.Background(), principal))
 	op.SetCancelFunc(cancel)
-	s.Set(opCtx, op)
+	_ = s.Set(opCtx, op)
 
 	go func() {
 		response, err := fn(opCtx)
@@ -137,11 +137,11 @@ func (s *service) Create(ctx context.Context, desc string, fn func(ctx context.C
 			op.SetError(err)
 		}
 		if response != nil {
-			op.SetResponse(response)
+			_ = op.SetResponse(response)
 		}
 		op.SetModifiedAt(time.Now())
 		op.SetDone(true)
-		s.Set(opCtx, op)
+		_ = s.Set(opCtx, op)
 	}()
 
 	return op, nil
diff --git a/pkg/organizations/events.go b/pkg/organizations/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..d477de46fe0e9ca360d6d61a080a01c9a6e13163
--- /dev/null
+++ b/pkg/organizations/events.go
@@ -0,0 +1,7 @@
+package organizations
+
+const (
+	EventCreate = "organizations.create"
+	EventDelete = "organizations.delete"
+	EventUpdate = "organizations.update"
+)
diff --git a/pkg/organizations/middleware/caching_middleware.go b/pkg/organizations/middleware/caching_middleware.go
index 18649df9511f12a658cbb3966d507a76108939d8..c6503200cfbc47915475a95c154a253dec51e29c 100644
--- a/pkg/organizations/middleware/caching_middleware.go
+++ b/pkg/organizations/middleware/caching_middleware.go
@@ -30,20 +30,21 @@ func (m cachingMiddleware) Get(ctx context.Context, orgId string) (organization
 
 	value, e := m.cache.Get(orgId)
 	if e == nil {
-		return value.(*service.Organization), err
+		return value.(*service.Organization).Clone(), nil
 	}
 	organization, err = m.next.Get(ctx, orgId)
 	if err == nil {
-		m.cache.Set(orgId, organization)
+		_ = m.cache.Set(orgId, organization)
+		return organization.Clone(), nil
 	}
-	return organization, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Update(ctx context.Context, org *service.Organization) (err error) {
 
 	err = m.next.Update(ctx, org)
 	if err == nil {
-		m.cache.Remove(org.ID)
+		_ = m.cache.Remove(org.ID)
 	}
 	return err
 }
@@ -52,7 +53,7 @@ func (m cachingMiddleware) Delete(ctx context.Context, orgId string) (err error)
 
 	err = m.next.Delete(ctx, orgId)
 	if err == nil {
-		m.cache.Remove(orgId)
+		_ = m.cache.Remove(orgId)
 	}
 	return err
 }
diff --git a/pkg/organizations/middleware/caching_middleware_test.go b/pkg/organizations/middleware/caching_middleware_test.go
index 19cd14d5b2e808f3d4c22f77d8b115a27a9025cf..f9999cfce509debda6b49d5aa5ee26a3d60d0f4f 100644
--- a/pkg/organizations/middleware/caching_middleware_test.go
+++ b/pkg/organizations/middleware/caching_middleware_test.go
@@ -37,7 +37,8 @@ func TestOrganizationsCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, orgId)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+		assert.NotSame(t, v1, v2)
 
 		orgs.AssertExpectations(t)
 	})
@@ -54,16 +55,18 @@ func TestOrganizationsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, orgId)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			orgs.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 			err = svc.Update(ctx, &organizations.Organization{ID: orgId, Name: "OrganizationUPD"})
+			require.NoError(t, err)
 
 			orgs.On("Get", mock.Anything, orgId).Return(&organizations.Organization{ID: orgId, Name: "OrganizationUPD"}, nil).Once()
 
 			v3, err := svc.Get(ctx, orgId)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v3, "Ожидается удаление объекта из кэша после обновления и получение заново из сервиса.")
+			assert.NotEqual(t, v2, v3, "Ожидается удаление объекта из кэша после обновления и получение заново из сервиса.")
 
 			orgs.AssertExpectations(t)
 		})
@@ -79,10 +82,12 @@ func TestOrganizationsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, orgId)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			orgs.On("Delete", mock.Anything, mock.Anything).Return(nil).Once()
 			err = svc.Delete(ctx, orgId)
+			require.NoError(t, err)
 
 			orgs.On("Get", mock.Anything, orgId).Return(nil, errNotFound).Once()
 
@@ -103,7 +108,8 @@ func TestOrganizationsCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, orgId)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 
@@ -112,6 +118,8 @@ func TestOrganizationsCache(t *testing.T) {
 			v3, err := svc.Get(ctx, orgId)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается удаление объекта из кэша и получение заново из сервиса.")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			orgs.AssertExpectations(t)
 		})
diff --git a/pkg/organizations/middleware/logging_middleware.go b/pkg/organizations/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..bda7f54fb101777758978a393bf43b52b1532b28
--- /dev/null
+++ b/pkg/organizations/middleware/logging_middleware.go
@@ -0,0 +1,108 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/organizations"
+
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   organizations.Organizations
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next organizations.Organizations) organizations.Organizations {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Organizations")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, org *organizations.Organization) (created *organizations.Organization, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(organizations.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, org)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(org))
+		return
+	}
+
+	logger.Info("Organization created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, orgId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(organizations.EventDelete),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	err = m.next.Delete(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Organization deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *organizations.Filter, opts *options.FindOptions) (orgs []*organizations.Organization, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	orgs, total, err = m.next.Find(ctx, filter, opts)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return orgs, total, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, orgId string) (org *organizations.Organization, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewOrganizationId(orgId)),
+	)
+
+	org, err = m.next.Get(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return org, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, org *organizations.Organization) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(organizations.EventUpdate),
+		logzap.Object(org),
+	)
+
+	err = m.next.Update(ctx, org)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Organization updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/organizations/middleware/middleware.go b/pkg/organizations/middleware/middleware.go
index 906a4c356b434fa2926a3e970bf0f32d0c0fa936..83808ec26d317f68a07b7b49c772911be1381fcb 100644
--- a/pkg/organizations/middleware/middleware.go
+++ b/pkg/organizations/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s organizations.Organizations, logger *zap.Logger, log_access bool)
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/organizations/organization.go b/pkg/organizations/organization.go
index b95daa6988710d26fcda200eebef8afeff9158df..8c6df31c766487d3978ef35e73d043343ab7d090 100644
--- a/pkg/organizations/organization.go
+++ b/pkg/organizations/organization.go
@@ -8,6 +8,16 @@ type Organization struct {
 	OwnerID     *string `bson:"-"`
 }
 
+func (o Organization) Clone() *Organization {
+	return &Organization{
+		ID:          o.ID,
+		Name:        o.Name,
+		Description: o.Description,
+		LogoURL:     o.LogoURL,
+		OwnerID:     o.OwnerID,
+	}
+}
+
 func (o *Organization) SetOwnerID(s string) *Organization {
 	o.OwnerID = &s
 	return o
diff --git a/pkg/organizations/transport/client.microgen.go b/pkg/organizations/transport/client.go
similarity index 67%
rename from pkg/organizations/transport/client.microgen.go
rename to pkg/organizations/transport/client.go
index 00b14af754877e02fe3936269d72cb13c7b138da..e08e3346129b8ec886f3eb83709c4047fd4eac43 100644
--- a/pkg/organizations/transport/client.microgen.go
+++ b/pkg/organizations/transport/client.go
@@ -4,21 +4,15 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	options "git.perx.ru/perxis/perxis-go/pkg/options"
 	organizations "git.perx.ru/perxis/perxis-go/pkg/organizations"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *organizations.Organization) (res0 *organizations.Organization, res1 error) {
 	request := CreateRequest{Org: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
@@ -28,9 +22,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string) (res0 *organizati
 	request := GetRequest{OrgId: arg1}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Org, res1
@@ -40,9 +31,6 @@ func (set EndpointsSet) Update(arg0 context.Context, arg1 *organizations.Organiz
 	request := UpdateRequest{Org: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -52,9 +40,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string) (res0 error) {
 	request := DeleteRequest{OrgId: arg1}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -67,9 +52,6 @@ func (set EndpointsSet) Find(arg0 context.Context, arg1 *organizations.Filter, a
 	}
 	response, res2 := set.FindEndpoint(arg0, &request)
 	if res2 != nil {
-		if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res2 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*FindResponse).Orgs, response.(*FindResponse).Total, res2
diff --git a/pkg/organizations/transport/grpc/client.go b/pkg/organizations/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..3e8793d1b81dd2c6e28b2c24a6be7b4d5b23ff95
--- /dev/null
+++ b/pkg/organizations/transport/grpc/client.go
@@ -0,0 +1,21 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/organizations/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ClientMiddleware(c.CreateEndpoint),
+		DeleteEndpoint: grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		FindEndpoint:   grpcerr.ClientMiddleware(c.FindEndpoint),
+		GetEndpoint:    grpcerr.ClientMiddleware(c.GetEndpoint),
+		UpdateEndpoint: grpcerr.ClientMiddleware(c.UpdateEndpoint),
+	}
+}
diff --git a/pkg/organizations/transport/grpc/server.go b/pkg/organizations/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..8748dabd8cd66b1c0597dce50b1cf43f87938d2e
--- /dev/null
+++ b/pkg/organizations/transport/grpc/server.go
@@ -0,0 +1,21 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/organizations"
+	"git.perx.ru/perxis/perxis-go/pkg/organizations/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/organizations"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc organizations.Organizations, opts ...grpckit.ServerOption) pb.OrganizationsServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		DeleteEndpoint: grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		FindEndpoint:   grpcerr.ServerMiddleware(eps.FindEndpoint),
+		GetEndpoint:    grpcerr.ServerMiddleware(eps.GetEndpoint),
+		UpdateEndpoint: grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/queue/queue.go b/pkg/queue/queue.go
index 34075f23240f0aff3002f1ad576fd204d620f9e7..adc8ed6e7e35b749a9b281484496e8ff7b991aa1 100644
--- a/pkg/queue/queue.go
+++ b/pkg/queue/queue.go
@@ -5,12 +5,12 @@ import (
 	"sync"
 	"time"
 
+	"git.perx.ru/perxis/perxis-go/id"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
-	"git.perx.ru/perxis/perxis-go/pkg/id"
 )
 
 const (
-	defaultSize            = 100
+	defaultSize            = 10_000
 	defaultStoreResultsTTL = 1 * time.Hour
 )
 
diff --git a/pkg/references/field.go b/pkg/references/field.go
index c98ed2298150e012545d55d5364f62c4f44eac1b..f80e7b5a564efe81df2969fb35f14d69fd23a1c8 100644
--- a/pkg/references/field.go
+++ b/pkg/references/field.go
@@ -18,17 +18,31 @@ type ReferenceParameters struct {
 }
 
 func (p ReferenceParameters) Type() field.Type { return &ReferenceType{} }
-
 func (p ReferenceParameters) Clone(reset bool) field.Parameters {
 	if p.AllowedCollections != nil {
 		cols := make([]string, 0, len(p.AllowedCollections))
-		for _, c := range p.AllowedCollections {
-			cols = append(cols, c)
-		}
+		cols = append(cols, p.AllowedCollections...)
 		p.AllowedCollections = cols
 	}
 	return &p
 }
+func (p ReferenceParameters) GetField(f *field.Field, name string) *field.Field {
+	var fld *field.Field
+	switch name {
+	case "id", "collection_id":
+		return field.String()
+	case "disabled":
+		return field.Bool()
+	}
+	return f.SetFieldState(name, fld)
+}
+func (p ReferenceParameters) ListFields(f *field.Field, filter ...field.FieldFilterFunc) []*field.Field {
+	return []*field.Field{
+		f.SetFieldState("id", field.String()),
+		f.SetFieldState("collection_id", field.String()),
+		f.SetFieldState("disabled", field.Bool()),
+	}
+}
 
 type ReferenceType struct{}
 
diff --git a/pkg/references/field_test.go b/pkg/references/field_test.go
index a51cb2ac65a3483cbe58169421734f17b6dce20c..ac9d5c03db107ed8f284cab3c9205afe95f1502b 100644
--- a/pkg/references/field_test.go
+++ b/pkg/references/field_test.go
@@ -255,7 +255,7 @@ func TestReferenceField_Validate(t *testing.T) {
 			refs := []*Reference{
 				{ID: "111", CollectionID: "media"},
 			}
-			err := validate.Validate(nil, f, refs)
+			err := validate.Validate(context.Background(), f, refs)
 			require.NoError(t, err)
 		})
 		t.Run("Limit exceeded", func(t *testing.T) {
@@ -264,7 +264,7 @@ func TestReferenceField_Validate(t *testing.T) {
 				{ID: "111", CollectionID: "media"},
 				{ID: "222", CollectionID: "media"},
 			}
-			err := validate.Validate(nil, f, refs)
+			err := validate.Validate(context.Background(), f, refs)
 			require.Error(t, err)
 			require.Contains(t, err.Error(), "validation error: maximum elements number is 1")
 		})
@@ -273,13 +273,13 @@ func TestReferenceField_Validate(t *testing.T) {
 		t.Run("Correct", func(t *testing.T) {
 			f := Field(nil, validate.Required())
 			ref := &Reference{ID: "111", CollectionID: "media"}
-			err := validate.Validate(nil, f, ref)
+			err := validate.Validate(context.Background(), f, ref)
 			require.NoError(t, err)
 		})
 		t.Run("Empty", func(t *testing.T) {
 			f := Field(nil, validate.Required())
 			ref := &Reference{}
-			err := validate.Validate(nil, f, ref)
+			err := validate.Validate(context.Background(), f, ref)
 			require.Error(t, err)
 			require.Contains(t, err.Error(), "validation error: value is required")
 		})
@@ -298,3 +298,23 @@ func TestReference_JSON(t *testing.T) {
 
 	assert.Equal(t, fld, res)
 }
+
+func TestReferenceField_Get(t *testing.T) {
+	sch := schema.New(
+		"a", field.String(),
+		"b", field.Object(
+			"ref", Field(nil),
+		),
+		"ref", Field(nil),
+	)
+	sch.ClearState()
+
+	assert.Equal(t, field.String(), sch.GetField("ref.id"))
+	assert.Equal(t, field.String(), sch.GetField("ref.collection_id"))
+	assert.Equal(t, field.Bool(), sch.GetField("ref.disabled"))
+
+	assert.Equal(t, field.String(), sch.GetField("b.ref.id"))
+	assert.Equal(t, field.String(), sch.GetField("b.ref.collection_id"))
+	assert.Equal(t, field.Bool(), sch.GetField("b.ref.disabled"))
+
+}
diff --git a/pkg/references/middleware/access_logging_middleware.go b/pkg/references/middleware/access_logging_middleware.go
index c628dc11fccc175b91f0ed7fdc7f180e3a91bfb4..b5fe116505c7a5f7d12c5f043c5d1c6b4c5d8fda 100644
--- a/pkg/references/middleware/access_logging_middleware.go
+++ b/pkg/references/middleware/access_logging_middleware.go
@@ -32,7 +32,7 @@ func AccessLoggingMiddleware(logger *zap.Logger) Middleware {
 	}
 }
 
-func (m *accessLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference) (items []*items.Item, notfound []*references.Reference, err error) {
+func (m *accessLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference, options ...*references.GetOptions) (items []*items.Item, notfound []*references.Reference, err error) {
 	begin := time.Now()
 
 	m.logger.Debug("Get.Request",
@@ -40,9 +40,10 @@ func (m *accessLoggingMiddleware) Get(ctx context.Context, spaceId string, envId
 		zap.Reflect("spaceId", spaceId),
 		zap.Reflect("envId", envId),
 		zap.Reflect("references", references),
+		zap.Reflect("options", options),
 	)
 
-	items, notfound, err = m.next.Get(ctx, spaceId, envId, references)
+	items, notfound, err = m.next.Get(ctx, spaceId, envId, references, options...)
 
 	m.logger.Debug("Get.Response",
 		zap.Duration("time", time.Since(begin)),
diff --git a/pkg/references/middleware/client_encode_middleware.go b/pkg/references/middleware/client_encode_middleware.go
index 33ca79610896926be3679920a6cd96c8afd6c73b..b6ea043b70da4ea5a9bcd3f3740ebb6e3774d107 100644
--- a/pkg/references/middleware/client_encode_middleware.go
+++ b/pkg/references/middleware/client_encode_middleware.go
@@ -23,8 +23,8 @@ type encodeDecodeMiddleware struct {
 	colls collections.Collections
 }
 
-func (m *encodeDecodeMiddleware) Get(ctx context.Context, spaceId, envId string, refs []*references.Reference) (items []*items.Item, notfound []*references.Reference, err error) {
-	items, notfound, err = m.next.Get(ctx, spaceId, envId, refs)
+func (m *encodeDecodeMiddleware) Get(ctx context.Context, spaceId, envId string, refs []*references.Reference, options ...*references.GetOptions) (items []*items.Item, notfound []*references.Reference, err error) {
+	items, notfound, err = m.next.Get(ctx, spaceId, envId, refs, options...)
 	if err == nil && len(items) > 0 {
 		for i, item := range items {
 			col, err := m.colls.Get(ctx, item.SpaceID, item.EnvID, item.CollectionID)
diff --git a/pkg/references/middleware/error_logging_middleware.go b/pkg/references/middleware/error_logging_middleware.go
index b55b11679d819e42f30234d27f6f140b2d024aa8..a68dc8f80685b704162c5399053a3bb3831846c9 100644
--- a/pkg/references/middleware/error_logging_middleware.go
+++ b/pkg/references/middleware/error_logging_middleware.go
@@ -30,14 +30,14 @@ func ErrorLoggingMiddleware(logger *zap.Logger) Middleware {
 	}
 }
 
-func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference) (items []*items.Item, notfound []*references.Reference, err error) {
+func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference, options ...*references.GetOptions) (items []*items.Item, notfound []*references.Reference, err error) {
 	logger := m.logger
 	defer func() {
 		if err != nil {
 			logger.Warn("response error", zap.Error(err))
 		}
 	}()
-	return m.next.Get(ctx, spaceId, envId, references)
+	return m.next.Get(ctx, spaceId, envId, references, options...)
 }
 
 func (m *errorLoggingMiddleware) Publish(ctx context.Context, spaceId string, envId string, references []*references.Reference, recursive bool, force bool) (published []*references.Reference, notfound []*references.Reference, unpublished []*references.Reference, err error) {
diff --git a/pkg/references/middleware/recovering_middleware.go b/pkg/references/middleware/recovering_middleware.go
index 1331a96787b8ba98322ab2e4ee5764d296504066..cc8a87ff40cd9f70da25444d53762cbce67e8fd7 100644
--- a/pkg/references/middleware/recovering_middleware.go
+++ b/pkg/references/middleware/recovering_middleware.go
@@ -31,7 +31,7 @@ func RecoveringMiddleware(logger *zap.Logger) Middleware {
 	}
 }
 
-func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference) (items []*items.Item, notfound []*references.Reference, err error) {
+func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference, options ...*references.GetOptions) (items []*items.Item, notfound []*references.Reference, err error) {
 	logger := m.logger
 	defer func() {
 		if r := recover(); r != nil {
@@ -40,7 +40,7 @@ func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, envId st
 		}
 	}()
 
-	return m.next.Get(ctx, spaceId, envId, references)
+	return m.next.Get(ctx, spaceId, envId, references, options...)
 }
 
 func (m *recoveringMiddleware) Publish(ctx context.Context, spaceId string, envId string, references []*references.Reference, recursive bool, force bool) (published []*references.Reference, notfound []*references.Reference, unpublished []*references.Reference, err error) {
diff --git a/pkg/references/middleware/telemetry_middleware.go b/pkg/references/middleware/telemetry_middleware.go
index f188bdc7ee04c13690ac0712e511508ccb2b12bc..71c51698bd9c015fdefd24e79b41ddcacda09eb2 100644
--- a/pkg/references/middleware/telemetry_middleware.go
+++ b/pkg/references/middleware/telemetry_middleware.go
@@ -1,10 +1,10 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ..\..\..\assets\templates\middleware\telemetry
+// template: ../../../assets/templates/middleware/telemetry
 // gowrap: http://github.com/hexdigest/gowrap
 
 package middleware
 
-//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/references -i References -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/references -i References -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
 
 // source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
@@ -50,7 +50,7 @@ func TelemetryMiddleware(base references.References, instance string, spanDecora
 }
 
 // Get implements references.References
-func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference) (items []*items.Item, notfound []*references.Reference, err error) {
+func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId string, references []*references.Reference, options ...*references.GetOptions) (items []*items.Item, notfound []*references.Reference, err error) {
 	attributes := otelmetric.WithAttributeSet(attribute.NewSet(
 		attribute.String("service", "References"),
 		attribute.String("method", "Get"),
@@ -69,7 +69,8 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str
 				"ctx":        ctx,
 				"spaceId":    spaceId,
 				"envId":      envId,
-				"references": references}, map[string]interface{}{
+				"references": references,
+				"options":    options}, map[string]interface{}{
 				"items":    items,
 				"notfound": notfound,
 				"err":      err})
@@ -83,7 +84,7 @@ func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string, envId str
 
 		_span.End()
 	}()
-	return _d.References.Get(ctx, spaceId, envId, references)
+	return _d.References.Get(ctx, spaceId, envId, references, options...)
 }
 
 // Publish implements references.References
diff --git a/pkg/references/mocks/Middleware.go b/pkg/references/mocks/Middleware.go
index a6d794c3c4a3d533b265768a187d39efe416052b..f2526e5012d4200d672b295a608156b3770d5e1f 100644
--- a/pkg/references/mocks/Middleware.go
+++ b/pkg/references/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.22.1. DO NOT EDIT.
+// Code generated by mockery v2.40.1. DO NOT EDIT.
 
 package mocks
 
@@ -16,6 +16,10 @@ type Middleware struct {
 func (_m *Middleware) Execute(_a0 references.References) references.References {
 	ret := _m.Called(_a0)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Execute")
+	}
+
 	var r0 references.References
 	if rf, ok := ret.Get(0).(func(references.References) references.References); ok {
 		r0 = rf(_a0)
@@ -28,13 +32,12 @@ func (_m *Middleware) Execute(_a0 references.References) references.References {
 	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/references/mocks/References.go b/pkg/references/mocks/References.go
index 4cd99f3d7b0c3a96a695e132e9f3ee65754b1dfe..92c8e29036b2960abd16b5f6df6caab04a783c0d 100644
--- a/pkg/references/mocks/References.go
+++ b/pkg/references/mocks/References.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.22.1. DO NOT EDIT.
+// Code generated by mockery v2.40.1. DO NOT EDIT.
 
 package mocks
 
@@ -16,34 +16,45 @@ type References struct {
 	mock.Mock
 }
 
-// Get provides a mock function with given fields: ctx, spaceId, envId, _a3
-func (_m *References) Get(ctx context.Context, spaceId string, envId string, _a3 []*references.Reference) ([]*items.Item, []*references.Reference, error) {
-	ret := _m.Called(ctx, spaceId, envId, _a3)
+// Get provides a mock function with given fields: ctx, spaceId, envId, _a3, options
+func (_m *References) Get(ctx context.Context, spaceId string, envId string, _a3 []*references.Reference, options ...*references.GetOptions) ([]*items.Item, []*references.Reference, error) {
+	_va := make([]interface{}, len(options))
+	for _i := range options {
+		_va[_i] = options[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, spaceId, envId, _a3)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Get")
+	}
 
 	var r0 []*items.Item
 	var r1 []*references.Reference
 	var r2 error
-	if rf, ok := ret.Get(0).(func(context.Context, string, string, []*references.Reference) ([]*items.Item, []*references.Reference, error)); ok {
-		return rf(ctx, spaceId, envId, _a3)
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, []*references.Reference, ...*references.GetOptions) ([]*items.Item, []*references.Reference, error)); ok {
+		return rf(ctx, spaceId, envId, _a3, options...)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, string, string, []*references.Reference) []*items.Item); ok {
-		r0 = rf(ctx, spaceId, envId, _a3)
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, []*references.Reference, ...*references.GetOptions) []*items.Item); ok {
+		r0 = rf(ctx, spaceId, envId, _a3, options...)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).([]*items.Item)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, string, string, []*references.Reference) []*references.Reference); ok {
-		r1 = rf(ctx, spaceId, envId, _a3)
+	if rf, ok := ret.Get(1).(func(context.Context, string, string, []*references.Reference, ...*references.GetOptions) []*references.Reference); ok {
+		r1 = rf(ctx, spaceId, envId, _a3, options...)
 	} else {
 		if ret.Get(1) != nil {
 			r1 = ret.Get(1).([]*references.Reference)
 		}
 	}
 
-	if rf, ok := ret.Get(2).(func(context.Context, string, string, []*references.Reference) error); ok {
-		r2 = rf(ctx, spaceId, envId, _a3)
+	if rf, ok := ret.Get(2).(func(context.Context, string, string, []*references.Reference, ...*references.GetOptions) error); ok {
+		r2 = rf(ctx, spaceId, envId, _a3, options...)
 	} else {
 		r2 = ret.Error(2)
 	}
@@ -55,6 +66,10 @@ func (_m *References) Get(ctx context.Context, spaceId string, envId string, _a3
 func (_m *References) Publish(ctx context.Context, spaceId string, envId string, _a3 []*references.Reference, recursive bool, force bool) ([]*references.Reference, []*references.Reference, []*references.Reference, error) {
 	ret := _m.Called(ctx, spaceId, envId, _a3, recursive, force)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Publish")
+	}
+
 	var r0 []*references.Reference
 	var r1 []*references.Reference
 	var r2 []*references.Reference
@@ -95,13 +110,12 @@ func (_m *References) Publish(ctx context.Context, spaceId string, envId string,
 	return r0, r1, r2, r3
 }
 
-type mockConstructorTestingTNewReferences interface {
+// NewReferences creates a new instance of References. 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 NewReferences(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewReferences creates a new instance of References. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewReferences(t mockConstructorTestingTNewReferences) *References {
+}) *References {
 	mock := &References{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/references/reference.go b/pkg/references/reference.go
index 75b9bc745d430ed0984ceedd07cf877adcb643e3..676f208c3304eb4840f43161a52a7a63779c6c8e 100644
--- a/pkg/references/reference.go
+++ b/pkg/references/reference.go
@@ -126,3 +126,45 @@ func EqualArrays(sr1, sr2 []*Reference) bool {
 func (r *Reference) IsValid() bool {
 	return r != nil && r.ID != "" && r.CollectionID != "" && !r.Disabled
 }
+
+type GetOptions struct {
+	LocaleID        string   `json:"locale_id,omitempty"`
+	TranslationsIDs []string `json:"translations_ids,omitempty"`
+}
+
+func NewGetOptions() *GetOptions {
+	return &GetOptions{}
+}
+
+func MergeGetOptions(opts ...*GetOptions) *GetOptions {
+	o := NewGetOptions()
+	for _, opt := range opts {
+		if opt == nil {
+			continue
+		}
+		o.LocaleID = opt.LocaleID
+		o.TranslationsIDs = append(o.TranslationsIDs, opt.TranslationsIDs...)
+	}
+	return o
+}
+
+func GetOptionsFromPB(opts *pb.GetOptions) *GetOptions {
+	if opts == nil {
+		return nil
+	}
+
+	return &GetOptions{
+		LocaleID:        opts.LocaleId,
+		TranslationsIDs: opts.TranslationsIds,
+	}
+}
+
+func GetOptionsToPB(opts *GetOptions) *pb.GetOptions {
+	if opts == nil {
+		return nil
+	}
+	return &pb.GetOptions{
+		LocaleId:        opts.LocaleID,
+		TranslationsIds: opts.TranslationsIDs,
+	}
+}
diff --git a/pkg/references/service.go b/pkg/references/service.go
index 136001a263b8754fba68de60259c2102608a0a77..2380a7879e0d15924ac83c226059932daf540bcf 100644
--- a/pkg/references/service.go
+++ b/pkg/references/service.go
@@ -10,7 +10,7 @@ import (
 // @protobuf git.perx.ru/perxis/perxis-go/proto/references
 // @grpc-addr content.references.References
 type References interface {
-	Get(ctx context.Context, spaceId, envId string, references []*Reference) (items []*items.Item, notfound []*Reference, err error)
+	Get(ctx context.Context, spaceId, envId string, references []*Reference, options ...*GetOptions) (items []*items.Item, notfound []*Reference, err error)
 
 	Publish(ctx context.Context, spaceId, envId string, references []*Reference, recursive, force bool) (published []*Reference, notfound []*Reference, unpublished []*Reference, err error)
 }
diff --git a/pkg/references/transport/client.microgen.go b/pkg/references/transport/client.go
similarity index 69%
rename from pkg/references/transport/client.microgen.go
rename to pkg/references/transport/client.go
index 5c0eb4143138bac3e2eecb3713103cf7c347c873..45f10f4f68c292729d7e461219219d09aa3f1341 100644
--- a/pkg/references/transport/client.microgen.go
+++ b/pkg/references/transport/client.go
@@ -4,25 +4,20 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	items "git.perx.ru/perxis/perxis-go/pkg/items"
 	references "git.perx.ru/perxis/perxis-go/pkg/references"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
-func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string, arg3 []*references.Reference) (res0 []*items.Item, res1 []*references.Reference, res2 error) {
+func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string, arg3 []*references.Reference, arg4 ...*references.GetOptions) (res0 []*items.Item, res1 []*references.Reference, res2 error) {
 	request := GetRequest{
 		EnvId:      arg2,
 		References: arg3,
 		SpaceId:    arg1,
+		Options:    arg4,
 	}
 	response, res2 := set.GetEndpoint(arg0, &request)
 	if res2 != nil {
-		if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res2 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Items, response.(*GetResponse).Notfound, res2
@@ -38,9 +33,6 @@ func (set EndpointsSet) Publish(arg0 context.Context, arg1 string, arg2 string,
 	}
 	response, res3 := set.PublishEndpoint(arg0, &request)
 	if res3 != nil {
-		if e, ok := status.FromError(res3); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res3 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*PublishResponse).Published, response.(*PublishResponse).Notfound, response.(*PublishResponse).Unpublished, res3
diff --git a/pkg/references/transport/exchanges.microgen.go b/pkg/references/transport/exchanges.microgen.go
index a0e1774d8b88deb4fb3393473e7efbebbc9a5118..4d0b51bf3f61286e8d4ed6968d3a831e2c57bbf0 100644
--- a/pkg/references/transport/exchanges.microgen.go
+++ b/pkg/references/transport/exchanges.microgen.go
@@ -9,9 +9,10 @@ import (
 
 type (
 	GetRequest struct {
-		SpaceId    string                  `json:"space_id"`
-		EnvId      string                  `json:"env_id"`
-		References []*references.Reference `json:"references"`
+		SpaceId    string                   `json:"space_id"`
+		EnvId      string                   `json:"env_id"`
+		References []*references.Reference  `json:"references"`
+		Options    []*references.GetOptions `json:"options"`
 	}
 	GetResponse struct {
 		Items    []*items.Item           `json:"items"`
diff --git a/pkg/references/transport/grpc/client.go b/pkg/references/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..01e5debd6080274fa42afe4779efc0190e8716ed
--- /dev/null
+++ b/pkg/references/transport/grpc/client.go
@@ -0,0 +1,18 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/references/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		GetEndpoint:     grpcerr.ClientMiddleware(c.GetEndpoint),
+		PublishEndpoint: grpcerr.ClientMiddleware(c.PublishEndpoint),
+	}
+}
diff --git a/pkg/references/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/references/transport/grpc/protobuf_endpoint_converters.microgen.go
index 0c995ffcb98960a8257533992cc5e33faaa9aa59..f2a2fad073ebb89f1b48242682066d0a8e171b10 100644
--- a/pkg/references/transport/grpc/protobuf_endpoint_converters.microgen.go
+++ b/pkg/references/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -7,6 +7,7 @@ import (
 	"context"
 	"errors"
 
+	"git.perx.ru/perxis/perxis-go/pkg/references"
 	"git.perx.ru/perxis/perxis-go/pkg/references/transport"
 	pb "git.perx.ru/perxis/perxis-go/proto/references"
 )
@@ -20,10 +21,15 @@ func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{},
 	if err != nil {
 		return nil, err
 	}
+	opts := &pb.GetOptions{}
+	if req.Options != nil {
+		opts = references.GetOptionsToPB(req.Options[0])
+	}
 	return &pb.GetRequest{
 		EnvId:      req.EnvId,
 		References: reqReferences,
 		SpaceId:    req.SpaceId,
+		Options:    opts,
 	}, nil
 }
 
@@ -59,6 +65,7 @@ func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{},
 		EnvId:      string(req.EnvId),
 		References: reqReferences,
 		SpaceId:    string(req.SpaceId),
+		Options:    []*references.GetOptions{references.GetOptionsFromPB(req.Options)},
 	}, nil
 }
 
diff --git a/pkg/references/transport/grpc/protobuf_type_converters.microgen.go b/pkg/references/transport/grpc/protobuf_type_converters.microgen.go
index 549d11725e085c4a35aaa047a2a4b1e9c43355df..965122b7da2c7e05db175f6f3a15ac373dd075b5 100644
--- a/pkg/references/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/references/transport/grpc/protobuf_type_converters.microgen.go
@@ -69,3 +69,25 @@ func ListPtrItemsItemToProto(items []*items.Item) ([]*itemspb.Item, error) {
 func ProtoToListPtrItemsItem(protoItems []*itemspb.Item) ([]*items.Item, error) {
 	return itemstransportgrpc.ProtoToListPtrItem(protoItems)
 }
+
+func ElPtrGetOptionsToProto(options []*service.GetOptions) ([]*pb.GetOptions, error) {
+	if options == nil {
+		return nil, nil
+	}
+	protoOption := make([]*pb.GetOptions, len(options))
+	for _, o := range options {
+		protoOption = append(protoOption, service.GetOptionsToPB(o))
+	}
+	return protoOption, nil
+}
+
+func ProtoToElPtrGetOptions(protoOptions []*pb.GetOptions) ([]*service.GetOptions, error) {
+	if protoOptions == nil {
+		return nil, nil
+	}
+	options := make([]*service.GetOptions, len(protoOptions))
+	for _, o := range protoOptions {
+		options = append(options, service.GetOptionsFromPB(o))
+	}
+	return options, nil
+}
diff --git a/pkg/references/transport/grpc/server.go b/pkg/references/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..32a4beac6e564c40d19a7cad937f8191fd8fe54c
--- /dev/null
+++ b/pkg/references/transport/grpc/server.go
@@ -0,0 +1,18 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/references"
+	"git.perx.ru/perxis/perxis-go/pkg/references/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/references"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc references.References, opts ...grpckit.ServerOption) pb.ReferencesServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		GetEndpoint:     grpcerr.ServerMiddleware(eps.GetEndpoint),
+		PublishEndpoint: grpcerr.ServerMiddleware(eps.PublishEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/references/transport/server.microgen.go b/pkg/references/transport/server.microgen.go
index 581aa61605034fa7fbb4cf025279ba6c38368d4a..06cf49df57605952311d211fc554123a86e760e5 100644
--- a/pkg/references/transport/server.microgen.go
+++ b/pkg/references/transport/server.microgen.go
@@ -19,7 +19,7 @@ func Endpoints(svc references.References) EndpointsSet {
 func GetEndpoint(svc references.References) endpoint.Endpoint {
 	return func(arg0 context.Context, request interface{}) (interface{}, error) {
 		req := request.(*GetRequest)
-		res0, res1, res2 := svc.Get(arg0, req.SpaceId, req.EnvId, req.References)
+		res0, res1, res2 := svc.Get(arg0, req.SpaceId, req.EnvId, req.References, req.Options...)
 		return &GetResponse{
 			Items:    res0,
 			Notfound: res1,
diff --git a/pkg/roles/events.go b/pkg/roles/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..c12631afb921a5ef4144e0c1d32d808051acddad
--- /dev/null
+++ b/pkg/roles/events.go
@@ -0,0 +1,7 @@
+package roles
+
+const (
+	EventCreate = "roles.create"
+	EventUpdate = "roles.update"
+	EventDelete = "roles.delete"
+)
diff --git a/pkg/roles/middleware/caching_middleware.go b/pkg/roles/middleware/caching_middleware.go
index 3f46087ec59e3ee71cd441b87e16a995d82eb7ea..2bcb04d3df7278d619fe80b2a6b02c48ea58f1fb 100644
--- a/pkg/roles/middleware/caching_middleware.go
+++ b/pkg/roles/middleware/caching_middleware.go
@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
 	service "git.perx.ru/perxis/perxis-go/pkg/roles"
 )
 
@@ -29,7 +30,7 @@ type cachingMiddleware struct {
 func (m cachingMiddleware) Create(ctx context.Context, role *service.Role) (rl *service.Role, err error) {
 	rl, err = m.next.Create(ctx, role)
 	if err == nil {
-		m.cache.Remove(rl.SpaceID)
+		_ = m.cache.Remove(rl.SpaceID)
 	}
 	return rl, err
 }
@@ -38,33 +39,35 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId string, roleId strin
 	key := makeKey(spaceId, roleId)
 	value, e := m.cache.Get(key)
 	if e == nil {
-		return value.(*service.Role), err
+		return value.(*service.Role).Clone(), nil
 	}
 	rl, err = m.next.Get(ctx, spaceId, roleId)
 	if err == nil {
-		m.cache.Set(key, rl)
+		_ = m.cache.Set(key, rl)
+		return rl.Clone(), nil
 	}
-	return rl, err
+	return nil, err
 }
 
 func (m cachingMiddleware) List(ctx context.Context, spaceId string) (roles []*service.Role, err error) {
 	value, e := m.cache.Get(spaceId)
 	if e == nil {
-		return value.([]*service.Role), err
+		return data.CloneSlice(value.([]*service.Role)), nil
 	}
 	roles, err = m.next.List(ctx, spaceId)
 	if err == nil {
-		m.cache.Set(spaceId, roles)
+		_ = m.cache.Set(spaceId, roles)
+		return data.CloneSlice(roles), nil
 	}
-	return roles, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Update(ctx context.Context, role *service.Role) (err error) {
 	err = m.next.Update(ctx, role)
 	if err == nil {
 		key := makeKey(role.SpaceID, role.ID)
-		m.cache.Remove(key)
-		m.cache.Remove(role.SpaceID)
+		_ = m.cache.Remove(key)
+		_ = m.cache.Remove(role.SpaceID)
 	}
 	return err
 }
@@ -73,8 +76,8 @@ func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, roleId st
 	err = m.next.Delete(ctx, spaceId, roleId)
 	if err == nil {
 		key := makeKey(spaceId, roleId)
-		m.cache.Remove(key)
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(key)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
diff --git a/pkg/roles/middleware/caching_middleware_test.go b/pkg/roles/middleware/caching_middleware_test.go
index 9d5d64f4231cf8bd86d533224fd50e762eebdcab..b9ac8ec826439319348bac2b13fc743c3753d024 100644
--- a/pkg/roles/middleware/caching_middleware_test.go
+++ b/pkg/roles/middleware/caching_middleware_test.go
@@ -39,7 +39,8 @@ func TestRolesCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID, roleID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+		assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+		assert.NotSame(t, v1, v2)
 
 		rl.AssertExpectations(t)
 	})
@@ -56,7 +57,8 @@ func TestRolesCache(t *testing.T) {
 
 		vl2, err := svc.List(ctx, spaceID)
 		require.NoError(t, err)
-		assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+		assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+		assert.NotSame(t, vl1[0], vl2[0])
 
 		rl.AssertExpectations(t)
 	})
@@ -75,14 +77,16 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, roleID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			rl.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 
@@ -94,11 +98,11 @@ func TestRolesCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID, roleID)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v3, "Ожидается что кеш объекта был удален после его обновления и объект был запрошен из сервиса.")
+			assert.NotEqual(t, v2, v3, "Ожидается что кеш объекта был удален после его обновления и объект был запрошен из сервиса.")
 
 			vl3, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
 
 			rl.AssertExpectations(t)
 		})
@@ -116,14 +120,16 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, roleID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			vl1, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			rl.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 
@@ -158,7 +164,8 @@ func TestRolesCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			rl.On("Create", mock.Anything, mock.Anything).Return(&roles.Role{ID: "roleID2", SpaceID: spaceID, Description: "Role2"}, nil).Once()
 
@@ -186,7 +193,8 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID, roleID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 			rl.On("Get", mock.Anything, spaceID, roleID).Return(&roles.Role{ID: roleID, SpaceID: spaceID, Description: "Role"}, nil).Once()
@@ -194,6 +202,8 @@ func TestRolesCache(t *testing.T) {
 			v3, err := svc.Get(ctx, spaceID, roleID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается что объект был удален из кеша и получен заново из сервиса.")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			rl.AssertExpectations(t)
 		})
diff --git a/pkg/roles/middleware/logging_middleware.go b/pkg/roles/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..9221265df1603f93be2c344c8c559eb8811e60de
--- /dev/null
+++ b/pkg/roles/middleware/logging_middleware.go
@@ -0,0 +1,106 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   roles.Roles
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next roles.Roles) roles.Roles {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Roles")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, role *roles.Role) (created *roles.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(roles.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, role)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(role))
+		return
+	}
+
+	logger.Info("Role created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId, roleId string) (role *roles.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewRoleId(spaceId, roleId)),
+	)
+
+	role, err = m.next.Get(ctx, spaceId, roleId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return role, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (roles []*roles.Role, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	roles, err = m.next.List(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return roles, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, role *roles.Role) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(roles.EventUpdate),
+		logzap.Object(role),
+	)
+
+	err = m.next.Update(ctx, role)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Role updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId, roleId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(roles.EventDelete),
+		logzap.Object(id.NewRoleId(spaceId, roleId)),
+	)
+
+	err = m.next.Delete(ctx, spaceId, roleId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Role deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/roles/middleware/middleware.go b/pkg/roles/middleware/middleware.go
index aaeb2da895d5aa71768e577315e549daa6a247c4..299199a40432f486d1020bb803f5bff18a95428e 100644
--- a/pkg/roles/middleware/middleware.go
+++ b/pkg/roles/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s roles.Roles, logger *zap.Logger, log_access bool) roles.Roles {
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/roles/role.go b/pkg/roles/role.go
index 370470675cd40ae417d38b2ae8b8f7c4e7a63cfe..76520f8fbf353c027ae782efb1bf79a1bfa453f5 100644
--- a/pkg/roles/role.go
+++ b/pkg/roles/role.go
@@ -2,6 +2,7 @@ package roles
 
 import (
 	"context"
+	"slices"
 
 	"git.perx.ru/perxis/perxis-go/pkg/data"
 	"git.perx.ru/perxis/perxis-go/pkg/environments"
@@ -34,6 +35,22 @@ type Role struct {
 	AllowManagement bool `json:"allow_management" bson:"allow_management"`
 }
 
+// GetID возвращает идентификатор роли
+func (r Role) GetID() string {
+	return r.ID
+}
+
+func (r Role) Clone() *Role {
+	return &Role{
+		ID:              r.ID,
+		SpaceID:         r.SpaceID,
+		Description:     r.Description,
+		Environments:    slices.Clone(r.Environments),
+		Rules:           data.CloneSlice(r.Rules),
+		AllowManagement: r.AllowManagement,
+	}
+}
+
 func (r Role) CanAccessEnvironment(ctx context.Context, env *environments.Environment, service environments.Environments) bool {
 	if env.SpaceID == "" || env.ID == "" {
 		return false
diff --git a/pkg/roles/transport/client.microgen.go b/pkg/roles/transport/client.go
similarity index 64%
rename from pkg/roles/transport/client.microgen.go
rename to pkg/roles/transport/client.go
index 71b1de9e2b1b746962effe61dc63c37cf7977e69..1b36431f5371bb93579f6bf985aa184337ac086b 100644
--- a/pkg/roles/transport/client.microgen.go
+++ b/pkg/roles/transport/client.go
@@ -4,20 +4,14 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	roles "git.perx.ru/perxis/perxis-go/pkg/roles"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *roles.Role) (res0 *roles.Role, res1 error) {
 	request := CreateRequest{Role: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).Created, res1
@@ -30,9 +24,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string, arg2 string) (res
 	}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).Role, res1
@@ -42,9 +33,6 @@ func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*roles.R
 	request := ListRequest{SpaceId: arg1}
 	response, res1 := set.ListEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*ListResponse).Roles, res1
@@ -54,9 +42,6 @@ func (set EndpointsSet) Update(arg0 context.Context, arg1 *roles.Role) (res0 err
 	request := UpdateRequest{Role: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -69,9 +54,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string, arg2 string) (
 	}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
diff --git a/pkg/roles/transport/grpc/client.go b/pkg/roles/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..06a23a7bceb96992c5114a46c1995eb5b4d8518c
--- /dev/null
+++ b/pkg/roles/transport/grpc/client.go
@@ -0,0 +1,21 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/roles/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ClientMiddleware(c.CreateEndpoint),
+		DeleteEndpoint: grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		GetEndpoint:    grpcerr.ClientMiddleware(c.GetEndpoint),
+		ListEndpoint:   grpcerr.ClientMiddleware(c.ListEndpoint),
+		UpdateEndpoint: grpcerr.ClientMiddleware(c.UpdateEndpoint),
+	}
+}
diff --git a/pkg/roles/transport/grpc/server.go b/pkg/roles/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..764efc8a4185e2078cac995a2a40a8010c94dd93
--- /dev/null
+++ b/pkg/roles/transport/grpc/server.go
@@ -0,0 +1,21 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"git.perx.ru/perxis/perxis-go/pkg/roles/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/roles"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc roles.Roles, opts ...grpckit.ServerOption) pb.RolesServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint: grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		DeleteEndpoint: grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		GetEndpoint:    grpcerr.ServerMiddleware(eps.GetEndpoint),
+		ListEndpoint:   grpcerr.ServerMiddleware(eps.ListEndpoint),
+		UpdateEndpoint: grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/pkg/schema/field/array.go b/pkg/schema/field/array.go
index 6700eb9b09af7bb714566dca8c82e90d026c7d57..bf1a4819dd075fc5e3f46fc4d1e289c8bd75cf90 100644
--- a/pkg/schema/field/array.go
+++ b/pkg/schema/field/array.go
@@ -22,6 +22,22 @@ func (p ArrayParameters) Clone(reset bool) Parameters {
 	return &ArrayParameters{Item: p.Item.Clone(reset)}
 }
 
+func (a ArrayParameters) GetField(f *Field, name string) *Field {
+	f.SetFieldState("Item", a.Item)
+
+	if name == "" || name == "Item" {
+		return a.Item
+	}
+
+	return a.Item.GetField(name)
+}
+
+func (a ArrayParameters) ListFields(f *Field, filterFunc ...FieldFilterFunc) []*Field {
+	f.SetFieldState("Item", a.Item)
+
+	return []*Field{a.Item}
+}
+
 type ArrayType struct{}
 
 func (ArrayType) Name() string {
@@ -116,17 +132,17 @@ func (ArrayType) Walk(ctx context.Context, field *Field, v interface{}, fn WalkF
 		return nil, false, nil
 	}
 
-	// Выполняется обход по схеме
-	if opts.WalkSchema && v == nil {
-		params.Item.Walk(ctx, v, fn, WalkOpts(opts))
-		return nil, false, nil
-	}
-
 	arr, ok := v.([]interface{})
-	if !ok {
+	if !ok && v != nil {
 		return nil, false, fmt.Errorf("incorrect type: \"%s\", expected \"[]interface{}\"", reflect.ValueOf(v).Kind())
 	}
 
+	// Выполняется обход по схеме
+	if opts.WalkSchema && len(arr) == 0 {
+		_, _, _ = params.Item.Walk(ctx, nil, fn, WalkOpts(opts))
+		return nil, false, nil
+	}
+
 	m := make([]interface{}, 0, len(arr))
 
 	var merr *multierror.Error
diff --git a/pkg/schema/field/array_test.go b/pkg/schema/field/array_test.go
index 94e60258661932dc72c2c34c27ea7cecb5c6b33d..b2948e1b12161da604ed22da96e4b8a092c0a99f 100644
--- a/pkg/schema/field/array_test.go
+++ b/pkg/schema/field/array_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"fmt"
 	"testing"
 
@@ -23,6 +24,26 @@ func TestArrayField_Decode(t *testing.T) {
 			[]interface{}{1.0, 2.0},
 			false,
 		},
+		{
+			"With object inside with nil-data",
+			Array(
+				Object("a", String(),
+					"b", String()),
+			),
+			nil,
+			nil,
+			false,
+		},
+		{
+			"With object inside with data",
+			Array(
+				Object("a", String(),
+					"b", String()),
+			),
+			[]interface{}{map[string]interface{}{"a": "1", "b": "2"}},
+			[]interface{}{map[string]interface{}{"a": "1", "b": "2"}},
+			false,
+		},
 		{
 			"Incorrect type",
 			Array(Number("int")),
@@ -33,7 +54,7 @@ func TestArrayField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Decode() error = %v, want %v", err, tt.want.(string)))
@@ -71,7 +92,7 @@ func TestArrayField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Decode() error = %v, want %v", err, tt.want.(string)))
@@ -83,3 +104,61 @@ func TestArrayField_Encode(t *testing.T) {
 		})
 	}
 }
+
+func TestArrayType_Walk(t *testing.T) {
+	tests := []struct {
+		name    string
+		field   *Field
+		v       interface{}
+		fn      WalkFunc
+		opts    *WalkOptions
+		want    interface{}
+		want1   bool
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name:    "With nil data and WalkSchema = false",
+			field:   Array(Object("a", String(), "b", String())),
+			v:       nil,
+			opts:    &WalkOptions{WalkSchema: false},
+			want:    nil,
+			want1:   false,
+			wantErr: assert.NoError,
+		},
+		{
+			name:  "With empty data and WalkSchema = false",
+			field: Array(Object("a", String(), "b", String())),
+			v:     []interface{}{map[string]interface{}{}},
+			opts:  &WalkOptions{WalkSchema: false},
+			fn: func(ctx context.Context, fld *Field, v interface{}) (result WalkFuncResult, err error) {
+				return WalkFuncResult{}, err
+			},
+			want:    []interface{}{map[string]interface{}{}},
+			want1:   false,
+			wantErr: assert.NoError,
+		},
+		{
+			name:  "With data and WalkSchema = false",
+			field: Array(Object("a", String(), "b", String())),
+			v:     []interface{}{map[string]interface{}{"a": "1", "b": "2"}},
+			opts:  &WalkOptions{WalkSchema: false},
+			fn: func(ctx context.Context, fld *Field, v interface{}) (result WalkFuncResult, err error) {
+				return WalkFuncResult{}, err
+			},
+			want:    []interface{}{map[string]interface{}{"a": "1", "b": "2"}},
+			want1:   false,
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ar := ArrayType{}
+			got, got1, err := ar.Walk(context.Background(), tt.field, tt.v, tt.fn, tt.opts)
+			if !tt.wantErr(t, err, fmt.Sprintf("Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)
+			assert.Equalf(t, tt.want1, got1, "Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)
+		})
+	}
+}
diff --git a/pkg/schema/field/boolean.go b/pkg/schema/field/boolean.go
index 94580541f8e33f9930adad94bff6a5c18915d05b..e06b704d0bb792d02a2f95343d037760fffb6fc7 100644
--- a/pkg/schema/field/boolean.go
+++ b/pkg/schema/field/boolean.go
@@ -10,8 +10,10 @@ var boolType = &BoolType{}
 
 type BoolParameters struct{}
 
-func (b BoolParameters) Type() Type                   { return boolType }
-func (b *BoolParameters) Clone(reset bool) Parameters { return b }
+func (b BoolParameters) Type() Type                                              { return boolType }
+func (b *BoolParameters) Clone(reset bool) Parameters                            { return b }
+func (b BoolParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (b BoolParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 type BoolType struct{}
 
diff --git a/pkg/schema/field/boolean_test.go b/pkg/schema/field/boolean_test.go
index aaa09dcec353d7648a976759bb4fcc98d0fe0d7a..857201f325680902a2c970010c8da5cc4ab6cadc 100644
--- a/pkg/schema/field/boolean_test.go
+++ b/pkg/schema/field/boolean_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"fmt"
 	"testing"
 
@@ -27,7 +28,7 @@ func TestBooleanField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Decode() error = %v, want %v", err, tt.want.(string)))
@@ -59,7 +60,7 @@ func TestBooleanField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Encode() error = %v, want %v", err, tt.want.(string)))
diff --git a/pkg/schema/field/field.go b/pkg/schema/field/field.go
index 45ddb40d128ab3f87ff7c41e3cedf75009ba3f98..4fc9516f1d6b64ce49ee42ea527b84992477365b 100644
--- a/pkg/schema/field/field.go
+++ b/pkg/schema/field/field.go
@@ -49,13 +49,24 @@ type Include struct {
 	Optional bool   `json:"optional,omitempty"`
 }
 
+// State - состояние поля времени выполнения
+type State struct {
+	Name         string
+	DataPath     string
+	SchemaPath   string
+	SingleLocale bool
+	Parent       *Field
+	Inlined      bool
+	HasInline    bool
+}
+
 type Field struct {
 	Title            string        `json:"title,omitempty"`             // Название поля (Например: name)
 	Description      string        `json:"description,omitempty"`       // Описание поле (Например: User name)
 	Translations     []Translation `json:"translations,omitempty"`      // Переводы данных на разных языках
 	UI               *UI           `json:"ui,omitempty"`                // Опции пользовательского интерфейса
 	Includes         []Include     `json:"includes,omitempty"`          // Импорт схем
-	SingleLocale     bool          `json:"singleLocale,omitempty"`      // Без перевода
+	SingleLocale     bool          `json:"single_locale,omitempty"`     // Без перевода
 	Indexed          bool          `json:"indexed,omitempty"`           // Построить индекс для поля
 	Unique           bool          `json:"unique,omitempty"`            // Значение поля должны быть уникальными
 	TextSearch       bool          `json:"text_search,omitempty"`       // Значение поля доступны для полнотекстового поиска
@@ -63,8 +74,7 @@ type Field struct {
 	Options          Options       `json:"options,omitempty"`           // Дополнительные опции
 	Condition        string        `json:"condition,omitempty"`         // Условие отображения поля
 	AdditionalValues bool          `json:"additional_values,omitempty"` // Разрешает дополнительные значения вне ограничений правил
-
-	prepared bool
+	State            *State        `json:"-"`                           // Состояние поля времени выполнения
 }
 
 // TODO: Replace with Named field???
@@ -86,7 +96,37 @@ func NewField(params Parameters, opts ...interface{}) *Field {
 	return f
 }
 
+// GetState возвращает состояние поля времени выполнения
+func (f Field) GetState() *State {
+	return f.State
+}
+
+// ClearState очищает состояние поля и всех вложенных полей
+//
+// Схемы нельзя сравнивать с активным состоянием с помощью `reflect.DeepEqual` или `assert.Equal`.
+// Предварительно нужно сделать `ClearState`.
+// После очистки состояния полей не будут рассчитываться. Для повторного включения состояния используйте `EnableState`
+func (f *Field) ClearState() *Field {
+	f.State = nil
+	for _, fld := range f.ListFields() {
+		fld.ClearState()
+	}
+	return f
+}
+
+// EnableState включает расчет состояния поля и всех вложенных полей
+//
+// Без включения состояния поля, невозможно получить доступ к данным времени выполнения
+// schema.New включает состояние для схемы при создании
+func (f *Field) EnableState() {
+	f.State = &State{}
+}
+
 func (f Field) GetType() Type {
+	if f.Params == nil {
+		return nil
+	}
+
 	return f.Params.Type()
 }
 
@@ -166,6 +206,10 @@ func (f Field) SetSingleLocale(r bool) *Field {
 	return &f
 }
 
+func (f Field) IsSingleLocale() bool {
+	return f.SingleLocale || (f.State != nil && f.State.SingleLocale)
+}
+
 func (f Field) SetIndexed(r bool) *Field {
 	f.Indexed = r
 	return &f
@@ -259,46 +303,91 @@ func (f *Field) Prepare() error {
 	return nil
 }
 
-// GetField возвращает поле по строковому пути
-func (f *Field) GetField(path string) *Field {
-	if path == "" {
-		switch params := f.Params.(type) {
-		case *ArrayParameters:
-			// Возвращаем поле Item если путь указан как "arr."
-			return params.Item
-		}
-		return nil
+func (f *Field) SetFieldState(name string, fld *Field) *Field {
+	if f != nil && fld != nil && f.State != nil && fld.State == nil {
+		fld.State = f.getFieldState(name, fld)
 	}
+	return fld
+}
 
-	switch params := f.Params.(type) {
-	case *ObjectParameters:
-		pp := strings.SplitN(path, FieldSeparator, 2)
+// GetFieldState возвращает состояние вложенного поля
+func (f *Field) getFieldState(name string, fld *Field) *State {
+	if f.State == nil {
+		return nil
+	}
 
-		for k, v := range params.Fields {
+	state := State{
+		SchemaPath: name,
+		DataPath:   name,
+		Name:       name,
+	}
 
-			p, ok := v.Params.(*ObjectParameters)
-			if ok && p.Inline {
-				f := v.GetField(path)
-				if f != nil {
-					return f
-				}
-			}
+	dataPath := f.State.DataPath
 
-			if k == pp[0] {
-				if len(pp) == 1 {
-					return v
-				}
-				return v.GetField(pp[1])
+	switch params := f.Params.(type) {
+	case *ObjectParameters:
+		if params.Inline {
+			last := strings.LastIndex(dataPath, ".")
+			if last > 0 {
+				dataPath = dataPath[:last]
+			} else {
+				dataPath = ""
 			}
+			state.Inlined = true
+		}
+		if dataPath != "" {
+			state.DataPath = dataPath + FieldSeparator + state.DataPath
 		}
-	case Fielder:
-		return params.GetField(path)
 
 	case *ArrayParameters:
-		return params.Item.GetField(path)
+		state.DataPath = dataPath // Remove item from path
 	}
 
-	return nil
+	state.SingleLocale = f.IsSingleLocale() || fld.SingleLocale
+
+	if f.State.SchemaPath != "" {
+		state.SchemaPath = f.State.SchemaPath + FieldSeparator + state.SchemaPath
+	}
+	state.Parent = f
+	state.HasInline = f.State.HasInline || state.Inlined
+	return &state
+}
+
+func (f *Field) GetFieldByName(name string) *Field {
+	return f.Params.GetField(f, name)
+}
+
+// GetField возвращает поле по строковому пути
+func (f *Field) GetField(path string) *Field {
+	name := ""
+	parts := strings.SplitN(path, FieldSeparator, 2)
+
+	if len(parts) > 0 {
+		name = parts[0]
+	}
+
+	fld := f.GetFieldByName(name)
+
+	if fld != nil && len(parts) > 1 {
+		return fld.GetField(parts[1])
+	}
+
+	return fld
+}
+
+// ListFields возвращает массив вложенных полей данного поля
+func (f *Field) ListFields(filter ...FieldFilterFunc) []*Field {
+	fields := f.Params.ListFields(f, filter...)
+	return fields
+}
+
+// ListFieldsRecursive возвращает массив всех вложенных полей рекурсивно
+func (f *Field) ListFieldsRecursive(filter ...FieldFilterFunc) []*Field {
+	fields := f.ListFields(filter...)
+	for _, fld := range f.ListFields() {
+		fields = append(fields, fld.ListFieldsRecursive(filter...)...)
+	}
+	return fields
 }
 
 // GetFieldsPath возвращает полный путь для массива полей
@@ -313,6 +402,8 @@ type FilterFunc func(*Field, string) bool
 
 func GetAll(field *Field, path string) bool { return true }
 
+// GetFields возвращает массив полей с путем???
+// DEPRECATED: использовать ListFields или ListFieldsRecursive
 func (f *Field) GetFields(filterFunc FilterFunc, pathPrefix ...string) (res []PathField) {
 	var path string
 
@@ -335,11 +426,11 @@ func (f *Field) GetFields(filterFunc FilterFunc, pathPrefix ...string) (res []Pa
 		res = append(res, getFieldsArray(path, params, filterFunc)...)
 	}
 
-	//if len(pathPrefix) > 0 {
+	// if len(pathPrefix) > 0 {
 	//	for _, r := range res {
 	//		r.Path = strings.Join([]string{pathPrefix[0], r.Path}, FieldSeparator)
 	//	}
-	//}
+	// }
 
 	return res
 }
@@ -384,22 +475,26 @@ func getFieldsObject(path string, params *ObjectParameters, filterFunc FilterFun
 	return res
 }
 
+// GetNestedFields возвращает вложенные поля
+// DEPRECATED: использовать ListFields
 func (f *Field) GetNestedFields() []*Field {
-	switch params := f.Params.(type) {
-	case *ObjectParameters:
-		flds := make([]*Field, 0, len(params.Fields))
-		for _, v := range params.Fields {
-			if v == nil {
-				continue
-			}
-			flds = append(flds, v)
-		}
-		return flds
-	case *ArrayParameters:
-		return []*Field{params.Item}
-	}
-
-	return nil
+	return f.ListFields()
+
+	//switch params := f.Params.(type) {
+	//case *ObjectParameters:
+	//	flds := make([]*Field, 0, len(params.Fields))
+	//	for _, v := range params.Fields {
+	//		if v == nil {
+	//			continue
+	//		}
+	//		flds = append(flds, v)
+	//	}
+	//	return flds
+	//case *ArrayParameters:
+	//	return []*Field{params.Item}
+	//}
+	//
+	//return nil
 }
 
 // Clone создает копию поля
@@ -477,7 +572,7 @@ func (f *Field) mergeField(fld *Field) error {
 
 func (f *Field) Merge(fields ...*Field) error {
 	for _, fld := range fields {
-		f.mergeField(fld)
+		_ = f.mergeField(fld)
 	}
 	return nil
 }
diff --git a/pkg/schema/field/field_json.go b/pkg/schema/field/field_json.go
index dfb212044838d66da90436b127594427d3f842da..4d5dfcb80fc5d6758056753ea49b8e294c76aeae 100644
--- a/pkg/schema/field/field_json.go
+++ b/pkg/schema/field/field_json.go
@@ -61,13 +61,19 @@ func (f *Field) UnmarshalJSON(b []byte) error {
 		}
 	}
 
+	j.FieldData.State = f.State
+
 	*f = Field(j.FieldData)
 	f.Params = params
-	f.Prepare()
+	_ = f.Prepare()
 	return nil
 }
 
 func (f *Field) MarshalJSON() ([]byte, error) {
+	if f.Params == nil {
+		return nil, errors.New("field parameters is nil")
+	}
+
 	j := jsonField{
 		FieldData: FieldData(*f),
 	}
diff --git a/pkg/schema/field/field_test.go b/pkg/schema/field/field_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..cb0458ed610a428abe84dc038bc4c172c5560bb9
--- /dev/null
+++ b/pkg/schema/field/field_test.go
@@ -0,0 +1,121 @@
+package field
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestField_GetField(t *testing.T) {
+	sch := Object(
+		"f1", Object(
+			"a", String(),
+			"b", String().SetSingleLocale(true),
+		),
+		"f3", Object( // inline object
+			true,
+			"a", String(),
+			"b", Object(true, "c", String()),
+		).SetSingleLocale(true),
+		"f4", Array(Object("a", String())),
+		"f5", Array(String()),
+		"f6", Object(true, "f6", Object("a", String())),
+	)
+
+	sch.EnableState()
+
+	tests := []struct {
+		name string
+		path string
+		want *State
+	}{
+		{"Object", "f1", &State{Name: "f1", DataPath: "f1", SchemaPath: "f1", Parent: sch}},
+		{"Object field", "f1.a", &State{Name: "a", DataPath: "f1.a", SchemaPath: "f1.a", Parent: sch.GetField("f1")}},
+		{"Field with SingleLocale", "f1.b", &State{Name: "b", DataPath: "f1.b", SchemaPath: "f1.b", Parent: sch.GetField("f1"), SingleLocale: true}},
+		{"Object with SingleLocale", "f3", &State{Name: "f3", DataPath: "f3", SchemaPath: "f3", Parent: sch, SingleLocale: true}},
+		{"Inline", "a", &State{Name: "a", DataPath: "a", SchemaPath: "f3.a", Parent: sch.GetField("f3"), SingleLocale: true, Inlined: true, HasInline: true}},
+		{"Inline of inline", "c", &State{Name: "c", DataPath: "c", SchemaPath: "f3.b.c", Parent: sch.GetField("f3.b"), SingleLocale: true, Inlined: true, HasInline: true}},
+		{"Inline of inline (direct)", "f3.b.c", &State{Name: "c", DataPath: "c", SchemaPath: "f3.b.c", Parent: sch.GetField("f3.b"), SingleLocale: true, Inlined: true, HasInline: true}},
+		{"Array of Objects", "f4", &State{Name: "f4", DataPath: "f4", SchemaPath: "f4", Parent: sch}},
+		{"Array of Objects (Item)", "f4.Item", &State{Name: "Item", DataPath: "f4", SchemaPath: "f4.Item", Parent: sch.GetField("f4")}},
+		{"Array of Objects (Item field)", "f4.Item.a", &State{Name: "a", DataPath: "f4.a", SchemaPath: "f4.Item.a", Parent: sch.GetField("f4.Item")}},
+		{"Array of Objects (Item field direct)", "f4.a", &State{Name: "a", DataPath: "f4.a", SchemaPath: "f4.Item.a", Parent: sch.GetField("f4.Item")}},
+		{"Array of Strings", "f5", &State{Name: "f5", DataPath: "f5", SchemaPath: "f5", Parent: sch}},
+		{"Array of Strings (Item)", "f5.Item", &State{Name: "Item", DataPath: "f5", SchemaPath: "f5.Item", Parent: sch.GetField("f5")}},
+		{"Inline Same name not found", "f6.a", nil},
+		{"Inline Same name (direct)", "f6.f6.a", &State{Name: "a", DataPath: "f6.a", SchemaPath: "f6.f6.a", Parent: sch.GetField("f6.f6"), HasInline: true}},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := sch.GetField(tt.path)
+			var st *State
+			if got != nil {
+				st = got.State
+			}
+
+			assert.Equal(t, tt.want, st)
+			sch.ClearState()
+			sch.EnableState()
+			if got != nil {
+				assert.Nil(t, got.State)
+			}
+		})
+	}
+}
+
+func TestField_ListFieldsRecursive(t *testing.T) {
+	sch := Object(
+		"f1", Object(
+			"f1a", String(),
+			"f1b", String().SetSingleLocale(true),
+		),
+		"f2", String(),
+		"f3", Object( // inline object
+			true,
+			"f3a", String(),
+			"f3b", Object(true, "f3bc", String()),
+		).SetSingleLocale(true),
+		"f4", Array(Object("f4a", String())),
+		"f5", Array(String()),
+		"f6", Object(true, "f6", Object("f6a", String())),
+	)
+
+	sch.EnableState()
+
+	fields := sch.ListFieldsRecursive()
+	assert.Len(t, fields, 16)
+	for _, f := range fields {
+		assert.NotNil(t, f.State)
+		assert.NotEmpty(t, f.State.Name)
+	}
+}
+
+func TestField_ListFieldsRecursive_WithFilter(t *testing.T) {
+	sch := Object(
+		"f1", Object(
+			"b", Object(
+				"c", String().SetSingleLocale(true),
+			),
+		),
+		"f2", Object(
+			"b", Object(
+				"c", String().SetSingleLocale(true),
+			),
+		).SetSingleLocale(true),
+	)
+
+	sch.EnableState()
+
+	fields := sch.ListFieldsRecursive(func(f *Field) bool { return f.SingleLocale == true })
+	assert.Len(t, fields, 3)
+}
+
+func TestField_CloneWithState(t *testing.T) {
+	f := Object("a", String())
+	fld := f.Clone(false)
+	assert.Nil(t, fld.State)
+	f.EnableState()
+	fld = f.Clone(false)
+	assert.NotNil(t, fld.State)
+}
diff --git a/pkg/schema/field/loader.go b/pkg/schema/field/loader.go
index 583ecc2c7c6bdc0d278819d2327280ec87858117..3beb0ddef0d68bdad2ec8f099dbfd5f853873c14 100644
--- a/pkg/schema/field/loader.go
+++ b/pkg/schema/field/loader.go
@@ -22,7 +22,7 @@ type multiLoader struct {
 
 func (c *multiLoader) Load(ctx context.Context, ref string) (fs []*Field, err error) {
 	for _, l := range c.loaders {
-		if f, err := l.Load(nil, ref); err == nil {
+		if f, err := l.Load(ctx, ref); err == nil {
 			return f, nil
 		}
 	}
diff --git a/pkg/schema/field/location.go b/pkg/schema/field/location.go
index d89b3206d091971a123e8842330f4b850838ccdf..97aea3ea8aecbf206325bbd1b49363669ce0436d 100644
--- a/pkg/schema/field/location.go
+++ b/pkg/schema/field/location.go
@@ -14,8 +14,10 @@ var locationType = &LocationType{}
 
 type LocationParameters struct{}
 
-func (p LocationParameters) Type() Type                  { return locationType }
-func (p LocationParameters) Clone(reset bool) Parameters { return &LocationParameters{} }
+func (p LocationParameters) Type() Type                                              { return locationType }
+func (p LocationParameters) Clone(reset bool) Parameters                             { return &LocationParameters{} }
+func (p LocationParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (p LocationParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 func (p LocationParameters) GetMongoIndexes(path string, f *Field) []mongo.IndexModel {
 	var add, geo mongo.IndexModel
diff --git a/pkg/schema/field/location_test.go b/pkg/schema/field/location_test.go
index 5b8206b6128ae7f4bf8f4042cedc0540bf357992..2680dec74e3f312abdaaf88d97a6ab6a255c1c8f 100644
--- a/pkg/schema/field/location_test.go
+++ b/pkg/schema/field/location_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"reflect"
 	"testing"
 )
@@ -114,7 +115,7 @@ func TestLocationField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -196,7 +197,7 @@ func TestLocationField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Encode() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/field/number.go b/pkg/schema/field/number.go
index 40d9c8167f54d23faaa40666ec1934b451d7a516..dfc8a6635cc0e719bd55e9f680ba23688674edd0 100644
--- a/pkg/schema/field/number.go
+++ b/pkg/schema/field/number.go
@@ -22,8 +22,10 @@ type NumberParameters struct {
 	Format string `json:"format,omitempty"`
 }
 
-func (NumberParameters) Type() Type                    { return numberType }
-func (p NumberParameters) Clone(reset bool) Parameters { return &p }
+func (NumberParameters) Type() Type                                                { return numberType }
+func (p NumberParameters) Clone(reset bool) Parameters                             { return &p }
+func (p NumberParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (p NumberParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 type NumberType struct{}
 
diff --git a/pkg/schema/field/object.go b/pkg/schema/field/object.go
index d86aa2352544f68ea837152ec6799677f4624d1a..45bc3e61d835cca2b8ba1a8866636ccd740c48c4 100644
--- a/pkg/schema/field/object.go
+++ b/pkg/schema/field/object.go
@@ -36,6 +36,39 @@ func (p ObjectParameters) Clone(reset bool) Parameters {
 	return &p
 }
 
+func (p ObjectParameters) GetField(f *Field, name string) *Field {
+	// Поиск поля в текущем объекте
+	if fld, ok := p.Fields[name]; ok {
+		return f.SetFieldState(name, fld)
+	}
+
+	// Поиск поля во вложенных Inline объектах
+	for k, v := range p.Fields {
+		if p, ok := v.Params.(*ObjectParameters); ok {
+			if p.Inline {
+				v = f.SetFieldState(k, v)
+				if fld := v.GetFieldByName(name); fld != nil {
+					return fld
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func (p ObjectParameters) ListFields(f *Field, filterFunc ...FieldFilterFunc) []*Field {
+	var fields []*Field
+	for k, fld := range p.Fields {
+		f.SetFieldState(k, fld)
+		if !ApplyFilterFunc(filterFunc, fld) {
+			continue
+		}
+		fields = append(fields, fld)
+	}
+	return fields
+}
+
 // IsInlineObject определяет являться ли поле name инлайн объектом
 func (p ObjectParameters) IsInlineObject(name string) bool {
 	fld, ok := p.Fields[name]
@@ -74,12 +107,18 @@ func (p *ObjectParameters) Merge(parameters Parameters) error {
 	if !ok {
 		return errors.New("invalid object parameters")
 	}
+	if op == nil {
+		return nil
+	}
 	for k, fld := range op.Fields {
 		if f, ok := p.Fields[k]; ok {
 			if err := f.Merge(fld); err != nil {
 				return err
 			}
 		} else {
+			if p.Fields == nil {
+				p.Fields = make(map[string]*Field)
+			}
 			p.Fields[k] = fld
 		}
 	}
diff --git a/pkg/schema/field/object_test.go b/pkg/schema/field/object_test.go
index 2ada594bcdeba09fab5dfcc95eb947e2028766d9..cf4d773bedc4c02694e425602f918131ee5a2655 100644
--- a/pkg/schema/field/object_test.go
+++ b/pkg/schema/field/object_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"fmt"
 	"testing"
 	"time"
@@ -77,7 +78,7 @@ func TestObjectField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Decode() error = %v, want %v", err, tt.want.(string)))
@@ -152,7 +153,7 @@ func TestObjectField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Encode() error = %v, want %v", err, tt.want.(string)))
@@ -230,3 +231,59 @@ func TestFieldNameValidate(t *testing.T) {
 		})
 	}
 }
+
+func TestObjectParameters_Merge(t *testing.T) {
+	tests := []struct {
+		name    string
+		l, r    *ObjectParameters
+		wantErr assert.ErrorAssertionFunc
+		want    *ObjectParameters
+	}{
+		{
+			name:    "right is nil",
+			l:       &ObjectParameters{},
+			r:       nil,
+			wantErr: assert.NoError,
+			want:    &ObjectParameters{},
+		},
+		{
+			name:    "right has nil Fields map",
+			l:       objectType.NewParameters().(*ObjectParameters),
+			r:       &ObjectParameters{Fields: nil},
+			wantErr: assert.NoError,
+			want:    &ObjectParameters{},
+		},
+		{
+			name:    "left + right is empty object",
+			l:       objectType.NewParameters().(*ObjectParameters),
+			r:       objectType.NewParameters().(*ObjectParameters),
+			wantErr: assert.NoError,
+			want:    &ObjectParameters{},
+		},
+		{
+			name:    "left + right with data",
+			l:       &ObjectParameters{Fields: map[string]*Field{"a": {Title: "Text1", Description: "test description"}}},
+			r:       &ObjectParameters{Fields: map[string]*Field{"b": {Title: "Text2", Description: "test description"}}},
+			wantErr: assert.NoError,
+			want: &ObjectParameters{Fields: map[string]*Field{
+				"a": {Title: "Text1", Description: "test description"},
+				"b": {Title: "Text2", Description: "test description"},
+			}},
+		},
+		{
+			name:    "left empty + right with data",
+			l:       &ObjectParameters{},
+			r:       &ObjectParameters{Fields: map[string]*Field{"b": {Title: "Text2", Description: "test description"}}},
+			wantErr: assert.NoError,
+			want: &ObjectParameters{Fields: map[string]*Field{
+				"b": {Title: "Text2", Description: "test description"},
+			}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tt.wantErr(t, tt.l.Merge(tt.r), fmt.Sprintf("Merge(%v)", tt.l))
+			assert.Equal(t, tt.l, tt.want)
+		})
+	}
+}
diff --git a/pkg/schema/field/primary_key.go b/pkg/schema/field/primary_key.go
index b0b26e16a91cd506231ba46307485290a138bfb8..da40f97eb65c8551b3ca2710a26812ececea616f 100644
--- a/pkg/schema/field/primary_key.go
+++ b/pkg/schema/field/primary_key.go
@@ -12,8 +12,10 @@ var primaryKeyType = &PrimaryKeyType{}
 
 type PrimaryKeyParameters struct{}
 
-func (p PrimaryKeyParameters) Type() Type                   { return primaryKeyType }
-func (p *PrimaryKeyParameters) Clone(reset bool) Parameters { return p }
+func (p PrimaryKeyParameters) Type() Type                                              { return primaryKeyType }
+func (p *PrimaryKeyParameters) Clone(reset bool) Parameters                            { return p }
+func (p PrimaryKeyParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (p PrimaryKeyParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 type PrimaryKeyType struct{}
 
diff --git a/pkg/schema/field/primary_key_test.go b/pkg/schema/field/primary_key_test.go
index f74f32cafc72612d9b1575561718e6c625334d53..5e11ff00f62f95306bf302da8ae8ba9a5a94fd30 100644
--- a/pkg/schema/field/primary_key_test.go
+++ b/pkg/schema/field/primary_key_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"reflect"
 	"testing"
 )
@@ -18,7 +19,7 @@ func TestPrimaryKeyField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -43,7 +44,7 @@ func TestPrimaryKeyField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/field/string.go b/pkg/schema/field/string.go
index b7e548b65f5c1572cc515cad19192899843ab62f..17b5e7ae086d3fac1b25d8c332c33d83fecedfb8 100644
--- a/pkg/schema/field/string.go
+++ b/pkg/schema/field/string.go
@@ -10,8 +10,10 @@ var stringType = &StringType{}
 
 type StringParameters struct{}
 
-func (s StringParameters) Type() Type                   { return stringType }
-func (s *StringParameters) Clone(reset bool) Parameters { return s }
+func (s StringParameters) Type() Type                                              { return stringType }
+func (s *StringParameters) Clone(reset bool) Parameters                            { return s }
+func (s StringParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (s StringParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 type StringType struct{}
 
diff --git a/pkg/schema/field/string_test.go b/pkg/schema/field/string_test.go
index d0fca29b8ed3f0c4ec987bf6705b7ea435116e40..16b7538fb86797946a4a3c3ba709e6e30cf4d5d8 100644
--- a/pkg/schema/field/string_test.go
+++ b/pkg/schema/field/string_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"reflect"
 	"testing"
 )
@@ -18,7 +19,7 @@ func TestStringField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -43,7 +44,7 @@ func TestStringField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/field/time.go b/pkg/schema/field/time.go
index 064906f236371d2914a0544c3f6abef83fd77f65..cdb50a4cafc500ea56dd56436faa5babd4793d31 100644
--- a/pkg/schema/field/time.go
+++ b/pkg/schema/field/time.go
@@ -17,8 +17,10 @@ type TimeParameters struct {
 	Layout string `json:"layout,omitempty"`
 }
 
-func (p TimeParameters) Type() Type                  { return timeType }
-func (p TimeParameters) Clone(reset bool) Parameters { return &p }
+func (p TimeParameters) Type() Type                                              { return timeType }
+func (p TimeParameters) Clone(reset bool) Parameters                             { return &p }
+func (p TimeParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (p TimeParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 func (p TimeParameters) GetLayout() string {
 	if p.Layout != "" {
diff --git a/pkg/schema/field/time_test.go b/pkg/schema/field/time_test.go
index f9a8fb44552a0e0a704f7baae7ab10019c889bec..d315850f7352c706fe9ae34a699795f892a558f4 100644
--- a/pkg/schema/field/time_test.go
+++ b/pkg/schema/field/time_test.go
@@ -1,6 +1,7 @@
 package field
 
 import (
+	"context"
 	"reflect"
 	"testing"
 	"time"
@@ -21,7 +22,7 @@ func TestTimeField_Decode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Decode(nil, tt.field, tt.data)
+			got, err := Decode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -48,7 +49,7 @@ func TestTimeField_Encode(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := Encode(nil, tt.field, tt.data)
+			got, err := Encode(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Encode() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/field/timestamp.go b/pkg/schema/field/timestamp.go
index 694d477e0c6a55e5140c4e7d78d26381a31686ae..593218c645c016751d082d1e938d0c966134ebb9 100644
--- a/pkg/schema/field/timestamp.go
+++ b/pkg/schema/field/timestamp.go
@@ -13,8 +13,10 @@ var (
 
 type TimestampParameters struct{}
 
-func (t TimestampParameters) Type() Type                   { return timestampType }
-func (t *TimestampParameters) Clone(reset bool) Parameters { return t }
+func (t TimestampParameters) Type() Type                                              { return timestampType }
+func (t *TimestampParameters) Clone(reset bool) Parameters                            { return t }
+func (t TimestampParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (t TimestampParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 type TimestampType struct{}
 
diff --git a/pkg/schema/field/type.go b/pkg/schema/field/type.go
index edae76734d562d2546d3b68e89e125c7a87744a6..984cd59187b8cd1f1380bcc0762d66366d4715ab 100644
--- a/pkg/schema/field/type.go
+++ b/pkg/schema/field/type.go
@@ -10,10 +10,23 @@ var (
 	registry sync.Map
 )
 
+type FieldFilterFunc func(f *Field) bool
+
+func ApplyFilterFunc(filterFunc []FieldFilterFunc, fld *Field) bool {
+	for _, f := range filterFunc {
+		if !f(fld) {
+			return false
+		}
+	}
+	return true
+}
+
 // Parameters - интерфейс который должен реализовывать параметр конкретного типа
 type Parameters interface {
 	Type() Type
 	Clone(reset bool) Parameters
+	GetField(f *Field, name string) *Field
+	ListFields(f *Field, filter ...FieldFilterFunc) []*Field
 }
 
 // Type - тип поля, отвечает за получение, кодирование и декодирование параметров для данного типа
diff --git a/pkg/schema/field/unknown.go b/pkg/schema/field/unknown.go
index f28a51cd1fbdc20a6b026d4504872149c939e727..36d0ba97fcd4b917af6e8986074d7f3f40e8654a 100644
--- a/pkg/schema/field/unknown.go
+++ b/pkg/schema/field/unknown.go
@@ -14,8 +14,10 @@ type UnknownParameters struct {
 	Params json.RawMessage `json:"params,omitempty"`
 }
 
-func (UnknownParameters) Type() Type                    { return unknownType }
-func (p UnknownParameters) Clone(reset bool) Parameters { return &p }
+func (UnknownParameters) Type() Type                                                { return unknownType }
+func (p UnknownParameters) Clone(reset bool) Parameters                             { return &p }
+func (p UnknownParameters) GetField(f *Field, name string) *Field                   { return nil }
+func (p UnknownParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
 
 type UnknownType struct{}
 
@@ -50,8 +52,7 @@ func (UnknownType) ConvertParameters(p Parameters) (Parameters, error) {
 }
 
 func Unknown(typ string, params json.RawMessage, o ...interface{}) *Field {
-	var pc ParametersConverter
-	pc = unknownType
+	pc := unknownType
 	_ = pc
 	return NewField(&UnknownParameters{Typ: typ, Params: params}, o...)
 }
diff --git a/pkg/schema/localizer/localizer.go b/pkg/schema/localizer/localizer.go
new file mode 100644
index 0000000000000000000000000000000000000000..4cc99d6075447c3eefe249f0b2963dca436c6e4f
--- /dev/null
+++ b/pkg/schema/localizer/localizer.go
@@ -0,0 +1,253 @@
+package localizer
+
+import (
+	"context"
+	"reflect"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"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/walk"
+)
+
+var (
+	ErrLocaleDisabled  = errors.New("cannot translate to disabled locale")
+	ErrLocaleNoPublish = errors.New("cannot translate to locale with disabled publication")
+)
+
+type Localizer struct {
+	schema           *schema.Schema
+	localesKV        map[string]*locales.Locale
+	localeID         string
+	locales          []*locales.Locale
+	allowNoPublished bool
+	allowDisabled    bool
+}
+
+type Config struct {
+	Schema           *schema.Schema
+	LocaleID         string
+	Locales          []*locales.Locale
+	AllowNoPublished bool
+	AllowDisabled    bool
+}
+
+// NewLocalizer создает экземпляр локализатора. Требуется указать "загруженную" схему
+func NewLocalizer(cfg Config) *Localizer {
+	if cfg.LocaleID == "" {
+		cfg.LocaleID = locales.DefaultID
+	}
+
+	loc := &Localizer{
+		schema:           cfg.Schema,
+		localesKV:        make(map[string]*locales.Locale, len(cfg.Locales)),
+		locales:          cfg.Locales,
+		localeID:         cfg.LocaleID,
+		allowDisabled:    cfg.AllowDisabled,
+		allowNoPublished: cfg.AllowNoPublished,
+	}
+
+	for _, l := range cfg.Locales {
+		loc.localesKV[l.ID] = l
+	}
+
+	return loc
+}
+
+// Localize Получить полные локализованные данные для локали `localeID`. Входные параметры:
+//   - `data map[string]interface{}` - данные основной локали
+//   - `translations map[string]map[string]interface{}` переводы
+//
+// При отсутствии каких-либо полей в переводе на `localeID` данные берутся сначала из fallback-локали,
+// если перевод отсутствует то из `data`
+func (l *Localizer) Localize(data map[string]interface{}, translations map[string]map[string]interface{}) (localized map[string]interface{}, err error) {
+	target, fallback, err := l.getTargetAndFallBackLocales()
+	if err != nil {
+		return nil, err
+	}
+
+	if target.IsDefault() {
+		return data, nil
+	}
+
+	// localize fallback -> target
+	fallbackData := data
+	var exist bool
+	if !fallback.IsDefault() {
+		if fd, exist := translations[fallback.ID]; exist {
+			// localize default -> fallback
+			fallbackData, err = l.localize(fd, data)
+			if err != nil {
+				return nil, err
+			}
+		}
+	}
+
+	if localized, exist = translations[target.ID]; !exist {
+		localized = make(map[string]interface{})
+	}
+
+	return l.localize(localized, fallbackData)
+}
+
+// ExtractTranslation Получить "просеянные" данные для локали localeID: все поля, значения которых совпадают
+// с переводом на fallback-локаль или основными данными, удаляются из перевода
+func (l *Localizer) ExtractTranslation(data map[string]interface{}, translations map[string]map[string]interface{}) (translation map[string]interface{}, err error) {
+	target, fallback, err := l.getTargetAndFallBackLocales()
+	if err != nil {
+		return nil, err
+	}
+
+	if target.IsDefault() {
+		return data, nil
+	}
+
+	var exist bool
+	if translation, exist = translations[target.ID]; !exist {
+		return make(map[string]interface{}), nil
+	}
+
+	var fallbackData map[string]interface{}
+	if fallbackData, exist = translations[fallback.ID]; !exist {
+		return l.extractTranslation(translation, data)
+	}
+
+	// localize default -> fallback - нужно для корректного сравнения нельзя делать просто extract из прореженных данных fallback
+	if fallbackData, err = l.localize(fallbackData, data); err != nil {
+		return nil, err
+	}
+
+	// extract translation default -> target
+	return l.extractTranslation(translation, fallbackData)
+}
+
+func (l *Localizer) locale(localeID string) (loc *locales.Locale, err error) {
+	if localeID == "" {
+		return nil, locales.ErrLocaleIDRequired
+	}
+
+	var exist bool
+	if loc, exist = l.localesKV[localeID]; !exist {
+		return nil, locales.ErrNotFound
+	}
+
+	if loc == nil {
+		return nil, locales.ErrNotFound
+	}
+
+	if !l.allowDisabled && loc.Disabled {
+		return nil, ErrLocaleDisabled
+	}
+
+	if !l.allowNoPublished && localeID == l.localeID && loc.NoPublish { // can use non-publishing locale for fallback
+		return nil, ErrLocaleNoPublish
+	}
+
+	return
+}
+
+func (l *Localizer) Locale() (loc *locales.Locale, err error) {
+	return l.locale(l.localeID)
+}
+
+func (l *Localizer) Locales() []*locales.Locale {
+	return l.locales
+}
+
+func (l *Localizer) LocaleID() string {
+	return l.localeID
+}
+
+func (l *Localizer) getTargetAndFallBackLocales() (target, fallback *locales.Locale, err error) {
+	if target, err = l.locale(l.localeID); err != nil {
+		return nil, nil, err
+	}
+
+	if fallback, err = l.locale(target.Fallback); err != nil {
+		if fallback, err = l.locale(locales.DefaultID); err != nil {
+			return nil, nil, errors.Wrap(err, "get default locale")
+		}
+	}
+	return
+}
+
+func (l *Localizer) localize(target, fallback map[string]interface{}) (map[string]interface{}, error) {
+	if target == nil && fallback == nil {
+		return nil, nil
+	}
+
+	single := l.schema.GetFields(func(f *field.Field, p string) bool {
+		return f.SingleLocale
+	})
+
+	cfg := &walk.WalkConfig{Fields: make(map[string]walk.FieldConfig, len(single))}
+	for _, sn := range single {
+		cfg.Fields[sn.Path] = walk.FieldConfig{Fn: walk.KeepSrc}
+	}
+
+	w := walk.NewWalker(l.schema, cfg)
+	w.DefaultFn = localize
+
+	res, _, err := w.DataWalk(context.Background(), target, fallback)
+	if err != nil {
+		return nil, err
+	}
+
+	if res != nil {
+		return res.(map[string]interface{}), err
+	}
+
+	return nil, nil
+}
+
+func (l *Localizer) extractTranslation(target, fallback map[string]interface{}) (map[string]interface{}, error) {
+	if target == nil && fallback == nil {
+		return nil, nil
+	}
+
+	single := l.schema.GetFields(func(f *field.Field, p string) bool {
+		return f.SingleLocale
+	})
+
+	cfg := &walk.WalkConfig{Fields: make(map[string]walk.FieldConfig, len(single))}
+	for _, sn := range single {
+		cfg.Fields[sn.Path] = walk.FieldConfig{Fn: walk.RemoveValue}
+	}
+
+	w := walk.NewWalker(l.schema, cfg)
+	w.DefaultFn = extractTranslation
+
+	res, _, err := w.DataWalk(context.Background(), target, fallback)
+	if err != nil {
+		return nil, err
+	}
+
+	if res == nil {
+		return map[string]interface{}{}, nil
+	}
+
+	return res.(map[string]interface{}), nil
+}
+
+func localize(c *walk.WalkContext) (err error) {
+	if c.Dst != nil {
+		return
+	}
+
+	c.Dst = c.Src
+	c.Changed = true
+	return
+}
+
+func extractTranslation(c *walk.WalkContext) (err error) {
+	if c.Dst == nil {
+		return
+	}
+
+	if reflect.DeepEqual(c.Src, c.Dst) {
+		c.Dst = nil
+		c.Changed = true
+	}
+	return
+}
diff --git a/pkg/schema/localizer/localizer_test.go b/pkg/schema/localizer/localizer_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ab6b277cdcad36a1ffdd217f1ec198a2bf958f9f
--- /dev/null
+++ b/pkg/schema/localizer/localizer_test.go
@@ -0,0 +1,938 @@
+package localizer
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestLocalizer_localize(t *testing.T) {
+
+	s := schema.New(
+		"a", field.String(),
+		"b", field.Number(field.NumberFormatInt),
+		"c", field.String().SetSingleLocale(true),
+		"obj1", field.Object(
+			"a", field.String(),
+			"b", field.Number(field.NumberFormatInt),
+			"c", field.Number(field.NumberFormatInt).SetSingleLocale(true),
+			"obj2", field.Object(
+				"a", field.Number(field.NumberFormatInt),
+				"b", field.String(),
+				"c", field.String().SetSingleLocale(true),
+			),
+		),
+		"obj3", field.Object(
+			"a", field.String(),
+		).SetSingleLocale(true),
+		"slice", field.Array(field.String()),
+		"arr", field.Array(field.Object(
+			"num", field.Number(field.NumberFormatInt),
+		)),
+	)
+
+	tests := []struct {
+		fallback map[string]interface{}
+		target   map[string]interface{}
+		want     map[string]interface{}
+		name     string
+		wantErr  bool
+	}{
+		{
+			name:     "Nil",
+			fallback: nil,
+			target:   nil,
+			want:     nil,
+		},
+		{
+			name:     "Empty",
+			fallback: map[string]interface{}{},
+			target:   map[string]interface{}{},
+			want:     map[string]interface{}{},
+		},
+		{
+			name: "Target Empty",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{},
+			want: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+		},
+		{
+			name:     "Fallback Empty",
+			fallback: map[string]interface{}{},
+			target: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			want: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+					},
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+		},
+		{
+			name: "Equal",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			want: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+		},
+		{
+			name: "Target no single",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+					},
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			want: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+		},
+		{
+			name: "Success",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{
+				"a": "ru",
+				"b": 11,
+				"obj1": map[string]interface{}{
+					"a": "obj1_ru",
+					"b": 22,
+					"obj2": map[string]interface{}{
+						"a": 33,
+						"b": "obj1_obj2_ru",
+					},
+				},
+				"slice": []interface{}{"ru_s3"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 1},
+					map[string]interface{}{"num": 2},
+				},
+			},
+			want: map[string]interface{}{
+				"a": "ru",
+				"b": 11,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_ru",
+					"b": 22,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 33,
+						"b": "obj1_obj2_ru",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"ru_s3"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 1},
+					map[string]interface{}{"num": 2},
+				},
+			},
+		},
+		{
+			name: "Success singlelocale obj",
+			fallback: map[string]interface{}{
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+			},
+			target: map[string]interface{}{
+				"obj3": map[string]interface{}{
+					"a": "obj3",
+				},
+			},
+			want: map[string]interface{}{
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			l := &Localizer{
+				schema: s,
+			}
+			got, err := l.localize(tt.target, tt.fallback)
+			if !tt.wantErr {
+				require.NoError(t, err)
+				assert.Equal(t, tt.want, got)
+				return
+			}
+			require.Error(t, err)
+		})
+	}
+}
+
+func TestLocalizer_extractTranslation(t *testing.T) {
+
+	s := schema.New(
+		"a", field.String(),
+		"b", field.Number(field.NumberFormatInt),
+		"c", field.String().SetSingleLocale(true),
+		"obj1", field.Object(
+			"a", field.String(),
+			"b", field.Number(field.NumberFormatInt),
+			"c", field.Number(field.NumberFormatInt).SetSingleLocale(true),
+			"obj2", field.Object(
+				"a", field.Number(field.NumberFormatInt),
+				"b", field.String(),
+				"c", field.String().SetSingleLocale(true),
+			),
+		),
+		"obj3", field.Object(
+			"a", field.String(),
+		).SetSingleLocale(true),
+		"slice", field.Array(field.String()),
+		"arr", field.Array(field.Object(
+			"num", field.Number(field.NumberFormatInt),
+		)),
+	)
+
+	tests := []struct {
+		fallback map[string]interface{}
+		target   map[string]interface{}
+		want     map[string]interface{}
+		name     string
+		wantErr  bool
+	}{
+		{
+			name:     "Nil",
+			fallback: nil,
+			target:   nil,
+			want:     nil,
+		},
+		{
+			name:     "Empty",
+			fallback: map[string]interface{}{},
+			target:   map[string]interface{}{},
+			want:     map[string]interface{}{},
+		},
+		{
+			name: "Target Empty",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{},
+			want:   map[string]interface{}{},
+		},
+		{
+			name:     "Fallback Empty",
+			fallback: map[string]interface{}{},
+			target: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			want: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+					},
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+		},
+		{
+			name: "Equal",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			want: map[string]interface{}{},
+		},
+		{
+			name: "Success",
+			fallback: map[string]interface{}{
+				"a": "en",
+				"b": 1,
+				"c": "single",
+				"obj1": map[string]interface{}{
+					"a": "obj1_en",
+					"b": 2,
+					"c": 20,
+					"obj2": map[string]interface{}{
+						"a": 3,
+						"b": "obj1_obj2_en",
+						"c": "obj1_obj2_single",
+					},
+				},
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+				"slice": []interface{}{"en_s1", "en_s2"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 11},
+					map[string]interface{}{"num": 22},
+				},
+			},
+			target: map[string]interface{}{
+				"a": "ru",
+				"b": 11,
+				"obj1": map[string]interface{}{
+					"a": "obj1_ru",
+					"b": 22,
+					"obj2": map[string]interface{}{
+						"a": 33,
+						"b": "obj1_obj2_ru",
+					},
+				},
+				"slice": []interface{}{"ru_s3"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 1},
+					map[string]interface{}{"num": 2},
+				},
+			},
+			want: map[string]interface{}{
+				"a": "ru",
+				"b": 11,
+				"obj1": map[string]interface{}{
+					"a": "obj1_ru",
+					"b": 22,
+					"obj2": map[string]interface{}{
+						"a": 33,
+						"b": "obj1_obj2_ru",
+					},
+				},
+				"slice": []interface{}{"ru_s3"},
+				"arr": []interface{}{
+					map[string]interface{}{"num": 1},
+					map[string]interface{}{"num": 2},
+				},
+			},
+		},
+		{
+			name: "Success singlelocale obj",
+			fallback: map[string]interface{}{
+				"obj3": map[string]interface{}{
+					"a": "obj3_en",
+				},
+			},
+			target: map[string]interface{}{
+				"obj3": map[string]interface{}{
+					"a": "obj3_ru",
+				},
+			},
+			want: map[string]interface{}{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			l := &Localizer{
+				schema: s,
+			}
+			got, err := l.extractTranslation(tt.target, tt.fallback)
+			if !tt.wantErr {
+				require.NoError(t, err)
+				assert.Equal(t, tt.want, got)
+				return
+			}
+			require.Error(t, err)
+		})
+	}
+}
+
+func TestLocalizer_getTargetAndFallBackLocales(t *testing.T) {
+
+	tests := []struct {
+		localizer    *Localizer
+		wantTarget   *locales.Locale
+		wantFallback *locales.Locale
+		name         string
+		wantErr      bool
+	}{
+		{
+			name:      "Empty",
+			localizer: &Localizer{},
+			wantErr:   true,
+		},
+		{
+			name: "Success",
+			localizer: &Localizer{
+				localeID: "en",
+				localesKV: map[string]*locales.Locale{
+					"en":      {ID: "en"},
+					"default": {ID: "default"},
+				},
+			},
+			wantTarget:   &locales.Locale{ID: "en"},
+			wantFallback: &locales.Locale{ID: "default"},
+		},
+		{
+			name: "Success fallback",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"en":      {ID: "en"},
+					"ru":      {ID: "ru", Fallback: "en"},
+					"default": {ID: "default"},
+				},
+			},
+			wantTarget:   &locales.Locale{ID: "ru", Fallback: "en"},
+			wantFallback: &locales.Locale{ID: "en"},
+		},
+		{
+			name: "Success fallback no default",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"en": {ID: "en"},
+					"ru": {ID: "ru", Fallback: "en"},
+				},
+			},
+			wantTarget:   &locales.Locale{ID: "ru", Fallback: "en"},
+			wantFallback: &locales.Locale{ID: "en"},
+		},
+		{
+			name: "Success fallback no fallback",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"ru":      {ID: "ru", Fallback: "en"},
+					"default": {ID: "default"},
+				},
+			},
+			wantTarget:   &locales.Locale{ID: "ru", Fallback: "en"},
+			wantFallback: &locales.Locale{ID: "default"},
+		},
+		{
+			name: "Fail fallback no fallback",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"ru": {ID: "ru", Fallback: "en"},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Fail target nil",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"ru": nil,
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Fail target disabled",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"ru":      {ID: "ru", Fallback: "en", Disabled: true},
+					"default": {ID: "default"},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Success fallback disabled",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"en":      {ID: "en", Disabled: true},
+					"ru":      {ID: "ru", Fallback: "en"},
+					"default": {ID: "default"},
+				},
+			},
+			wantTarget:   &locales.Locale{ID: "ru", Fallback: "en"},
+			wantFallback: &locales.Locale{ID: "default"},
+		},
+		{
+			name: "Fail target no publish",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"ru":      {ID: "ru", Fallback: "en", NoPublish: true},
+					"default": {ID: "default"},
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "Success fallback no publish",
+			localizer: &Localizer{
+				localeID: "ru",
+				localesKV: map[string]*locales.Locale{
+					"en":      {ID: "en", NoPublish: true},
+					"ru":      {ID: "ru", Fallback: "en"},
+					"default": {ID: "default"},
+				},
+			},
+			wantTarget:   &locales.Locale{ID: "ru", Fallback: "en"},
+			wantFallback: &locales.Locale{ID: "en", NoPublish: true},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotTarget, gotFallback, err := tt.localizer.getTargetAndFallBackLocales()
+			if tt.wantErr {
+				require.Error(t, err)
+				return
+			}
+			require.NoError(t, err)
+			assert.Equal(t, tt.wantTarget, gotTarget)
+			assert.Equal(t, tt.wantFallback, gotFallback)
+		})
+	}
+}
+
+func TestLocalizer_ExtractTranslations(t *testing.T) {
+
+	s := schema.New(
+		"a", field.String(),
+		"b", field.String(),
+	)
+
+	tests := []struct {
+		data         map[string]interface{}
+		translations map[string]map[string]interface{}
+		want         map[string]interface{}
+		cfg          Config
+		name         string
+		wantErr      bool
+	}{
+		{
+			name:         "Extract default",
+			data:         map[string]interface{}{"a": "bbb"},
+			translations: map[string]map[string]interface{}{"en": {"a": "bbb"}},
+			cfg: Config{
+				LocaleID: "en",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{},
+		},
+		{
+			name:         "Extract default not same",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"en": {"a": "bbb"}},
+			cfg: Config{
+				LocaleID: "en",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{"a": "bbb"},
+		},
+		{
+			name:         "Extract default empty",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"en": {}},
+			cfg: Config{
+				LocaleID: "en",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{},
+		},
+		{
+			name:         "Extract fallback",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"ru": {"a": "bbb"}, "en": {"a": "bbb"}},
+			cfg: Config{
+				LocaleID: "ru",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "ru", Fallback: "en"},
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{},
+		},
+		{
+			name:         "Extract fallback with extracted field",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"ru": {"a": "bbb"}, "en": {}},
+			cfg: Config{
+				LocaleID: "ru",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "ru", Fallback: "en"},
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{"a": "bbb"},
+		},
+		{
+			name:         "Extract fallback with empty field",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"ru": {}, "en": {"a": "bbb"}},
+			cfg: Config{
+				LocaleID: "ru",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "ru", Fallback: "en"},
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{},
+		},
+		{
+			name:         "Extract fallback with same as default",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"ru": {"a": "aaa"}, "en": {"a": "bbb"}},
+			cfg: Config{
+				LocaleID: "ru",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "ru", Fallback: "en"},
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{"a": "aaa"},
+		},
+		{
+			name:         "Extract fallback with same",
+			data:         map[string]interface{}{"a": "aaa"},
+			translations: map[string]map[string]interface{}{"ru": {"a": "aaa"}, "en": {"a": "aaa", "b": "bbb"}},
+			cfg: Config{
+				LocaleID: "ru",
+				Schema:   s,
+				Locales: []*locales.Locale{
+					{ID: "ru", Fallback: "en"},
+					{ID: "en"},
+					{ID: "default"},
+				},
+			},
+			want: map[string]interface{}{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			l := NewLocalizer(tt.cfg)
+			got, err := l.ExtractTranslation(tt.data, tt.translations)
+			if !tt.wantErr {
+				require.NoError(t, err)
+				assert.Equal(t, tt.want, got)
+				return
+			}
+			require.Error(t, err)
+		})
+	}
+}
diff --git a/pkg/schema/modify/default.go b/pkg/schema/modify/default.go
index 66ce6b89c0619ceb232050fa5ab057e4330fdfcb..637a0ff86021b61a59621a6aea7f63d89a40db64 100644
--- a/pkg/schema/modify/default.go
+++ b/pkg/schema/modify/default.go
@@ -14,7 +14,7 @@ type defaultValue struct {
 
 func (d *defaultValue) Prepare(f *field.Field) error {
 	var err error
-	d.Value, err = field.Decode(nil, f, d.Value)
+	d.Value, err = field.Decode(context.Background(), f, d.Value)
 	return err
 }
 
diff --git a/pkg/schema/modify/default_test.go b/pkg/schema/modify/default_test.go
index b99630dc65a9249d04bc1b29ac9e9fc902c745cf..6ea9d11047c13434553ae0ee7776a49ab0ffaf44 100644
--- a/pkg/schema/modify/default_test.go
+++ b/pkg/schema/modify/default_test.go
@@ -1,6 +1,7 @@
 package modify
 
 import (
+	"context"
 	"testing"
 	"time"
 
@@ -45,9 +46,9 @@ func TestDefault(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			got, _, err = Modify(nil, tt.field, got)
+			got, _, err = Modify(context.Background(), tt.field, got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Modify() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/modify/string_test.go b/pkg/schema/modify/string_test.go
index ef90f7f6f4d6a43ff13b3ac10bda68602f09c5f4..2f6451266aa08612718321d1b85d4341f280ee74 100644
--- a/pkg/schema/modify/string_test.go
+++ b/pkg/schema/modify/string_test.go
@@ -1,6 +1,7 @@
 package modify
 
 import (
+	"context"
 	"reflect"
 	"testing"
 
@@ -25,7 +26,7 @@ func TestTrimSpace(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, _, err := Modify(nil, tt.field, tt.data)
+			got, _, err := Modify(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Modify() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -65,7 +66,7 @@ func TestModify(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			_, _, err := Modify(nil, tt.field, tt.data)
+			_, _, err := Modify(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.error)
diff --git a/pkg/schema/modify/value_test.go b/pkg/schema/modify/value_test.go
index 9878fc70663005f7650ac567899634a94446e8b9..eb3b9a99f6d4df61f738a993c35b61be2ea865eb 100644
--- a/pkg/schema/modify/value_test.go
+++ b/pkg/schema/modify/value_test.go
@@ -1,6 +1,7 @@
 package modify
 
 import (
+	"context"
 	"reflect"
 	"testing"
 
@@ -50,7 +51,7 @@ func TestValue(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, _, err := Modify(nil, tt.field, tt.data)
+			got, _, err := Modify(context.Background(), tt.field, tt.data)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Modify() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go
index e45298241602e6855c63019fe5bebcc4e2df27ee..fdb402b412de15b3ba8aa9d2b0244ea3d22e8f09 100644
--- a/pkg/schema/schema.go
+++ b/pkg/schema/schema.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"reflect"
 
+	"git.perx.ru/perxis/perxis-go"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/expr"
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
@@ -18,7 +19,9 @@ type Schema struct {
 }
 
 func New(kv ...interface{}) *Schema {
-	return &Schema{Field: *field.Object(kv...)}
+	s := &Schema{Field: *field.Object(kv...)}
+	s.Field.EnableState()
+	return s
 }
 
 func NewFromField(f *field.Field) *Schema {
@@ -31,6 +34,10 @@ var (
 	Modify   = modify.Modify
 	Validate = validate.Validate
 	Evaluate = field.Evaluate
+
+	Assets   = perxis.NewAssets[*Schema]().WithConstructor(func() *Schema { return New() })
+	FromFS   = Assets.FromFS
+	FromFile = Assets.FromFile
 )
 
 func (s *Schema) Clone(reset bool) *Schema {
@@ -41,6 +48,11 @@ func (s *Schema) Clone(reset bool) *Schema {
 	}
 }
 
+func (s *Schema) ClearState() *Schema {
+	s.Field.ClearState()
+	return s
+}
+
 func (s *Schema) Equal(sch *Schema) bool {
 	if s == sch {
 		return true
@@ -71,12 +83,20 @@ func (s Schema) SetMetadata(md map[string]string) *Schema {
 	return &s
 }
 
+func (f Schema) SetSingleLocale(r bool) *Schema {
+	f.SingleLocale = r
+	return &f
+}
+
 func (s *Schema) ConvertTypes() error {
 	b, err := s.MarshalJSON()
 	if err != nil {
 		return errors.Wrap(err, "marshal schema")
 	}
+	// сохраняем состояние cхемы
+	state := s.Field.State
 	*s = *New()
+	s.Field.State = state
 	return errors.Wrap(s.UnmarshalJSON(b), "unmarshal schema")
 }
 
@@ -222,7 +242,7 @@ func (s *Schema) Introspect(ctx context.Context, data map[string]interface{}) (m
 			}
 
 			if parent != nil && name != "" {
-				field.AddField(parent, name, fld)
+				_ = field.AddField(parent, name, fld)
 			}
 
 			ctx = context.WithValue(ctx, parentFieldCtxKey{}, fld)
@@ -250,3 +270,12 @@ func (s *Schema) Introspect(ctx context.Context, data map[string]interface{}) (m
 
 	return val, mutatedSchema, nil
 }
+
+// GetEnum возвращает список опций перечисления для поля
+func (s *Schema) GetEnum(fieldPath string) []validate.EnumOpt {
+	f := s.Field.GetField(fieldPath)
+	if f == nil {
+		return nil
+	}
+	return validate.GetEnum(f)
+}
diff --git a/pkg/schema/schema_json.go b/pkg/schema/schema_json.go
index e8710f76dfb8a5a81da279e4ba6d46f77a1bbdb8..0fe8a50b061cecabef84d358b53751139e6a5b21 100644
--- a/pkg/schema/schema_json.go
+++ b/pkg/schema/schema_json.go
@@ -1,10 +1,23 @@
 package schema
 
 import (
+	"io"
+
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	jsoniter "github.com/json-iterator/go"
 )
 
+func FromJSON(r io.Reader) (s *Schema, err error) {
+	data, err := io.ReadAll(r)
+	if err != nil {
+		return nil, err
+	}
+
+	s = New()
+	err = s.UnmarshalJSON(data)
+	return s, err
+}
+
 type jsonSchema struct {
 	Loaded   bool              `json:"loaded"`
 	Metadata map[string]string `json:"metadata"`
@@ -16,6 +29,11 @@ func (s *Schema) UnmarshalJSON(b []byte) error {
 	if err := jsoniter.Unmarshal(b, &j); err != nil {
 		return errors.Wrapf(err, "error unmarshal json into field")
 	}
+
+	if j == nil {
+		return nil
+	}
+
 	s.Loaded = j.Loaded
 	s.Metadata = j.Metadata
 
@@ -23,15 +41,6 @@ func (s *Schema) UnmarshalJSON(b []byte) error {
 		return err
 	}
 
-	//if len(j.Field) > 0 {
-	//	if err := s.Field.UnmarshalJSON(j.Field); err != nil {
-	//		return err
-	//	}
-	//	//if err := jsoniter.Unmarshal(j.Field, &s.Field); err != nil {
-	//	//	return err
-	//	//}
-	//}
-
 	return nil
 }
 
diff --git a/pkg/schema/schema_json_test.go b/pkg/schema/schema_json_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..31b475168e9e0179328295044a7201517212d4ec
--- /dev/null
+++ b/pkg/schema/schema_json_test.go
@@ -0,0 +1,78 @@
+package schema
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSchema_UnmarshalJSON(t *testing.T) {
+	s := `{
+    "ui": {
+        "options": {
+            "fields": [
+                "text"
+            ]
+        }
+    },
+    "type": "object",
+    "params": {
+        "inline": false,
+        "fields": {
+            "text": {
+                "title": "text",
+                "ui": {
+                    "widget": "StringInput"
+                },
+                "type": "string",
+                "params": {}
+            }
+        }
+    },
+    "loaded": false,
+    "metadata": null
+}`
+	tests := []struct {
+		name    string
+		b       []byte
+		want    *Schema
+		wantErr bool
+	}{
+		{
+			name:    "No panic with 'null' value in b",
+			b:       []byte("null"),
+			want:    New(),
+			wantErr: false,
+		},
+		{
+			name:    "UnmarshalJSON error #1",
+			b:       []byte(""),
+			want:    New(),
+			wantErr: true,
+		},
+		{
+			name:    "UnmarshalJSON error #2",
+			b:       []byte("abc"),
+			want:    New(),
+			wantErr: true,
+		},
+		{
+			name:    "OK",
+			b:       []byte(s),
+			want:    New("text", field.String().SetTitle("text").WithUI(&field.UI{Widget: "StringInput"})),
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			schema := New()
+			if err := schema.UnmarshalJSON(tt.b); (err != nil) != tt.wantErr {
+				t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			schema.ClearState()
+			tt.want.ClearState()
+			assert.Equal(t, tt.want, schema)
+		})
+	}
+}
diff --git a/pkg/schema/schema_test.go b/pkg/schema/schema_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..dbedf48f9667c348c0898b73a3d308312aede6c6
--- /dev/null
+++ b/pkg/schema/schema_test.go
@@ -0,0 +1,18 @@
+package schema
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSchema_Clone(t *testing.T) {
+	f := New("a", field.String())
+	f.ClearState()
+	fld := f.Clone(false)
+	assert.Nil(t, fld.State)
+	f.EnableState()
+	fld = f.Clone(false)
+	assert.NotNil(t, fld.State)
+}
diff --git a/pkg/schema/test/assets/invalid.json b/pkg/schema/test/assets/invalid.json
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pkg/schema/test/assets/not_schema.txt b/pkg/schema/test/assets/not_schema.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pkg/schema/test/assets/web_pages.json b/pkg/schema/test/assets/web_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..652753c911e79dca0896b2c57587400202d0da69
--- /dev/null
+++ b/pkg/schema/test/assets/web_pages.json
@@ -0,0 +1,420 @@
+{
+    "ui": {
+        "widget": "Tabs",
+        "options": {
+            "description": "path",
+            "collection_icon": "ApartmentOutlined/FileTextOutlined",
+            "fields": [
+                "content",
+                "seo",
+                "settings",
+                "advanced",
+                "design",
+                "variables"
+            ],
+            "title": "name",
+            "key": "path"
+        },
+        "list_view": {
+            "options": {
+                "sort": [
+                    "path"
+                ],
+                "page_size": 50,
+                "fields": [
+                    "path",
+                    "name",
+                    "updated_at",
+                    "updated_by",
+                    "state"
+                ]
+            }
+        }
+    },
+    "includes": [
+        {
+            "ref": "hoop_item_options",
+            "optional": true
+        },
+        {
+            "ref": "ext_web_pages_*",
+            "optional": true
+        }
+    ],
+    "type": "object",
+    "params": {
+        "inline": false,
+        "fields": {
+            "design": {
+                "title": "Дизайн",
+                "includes": [
+                    {
+                        "ref": "web_design"
+                    }
+                ],
+                "type": "object",
+                "params": {
+                    "inline": false,
+                    "fields": {}
+                }
+            },
+            "variables": {
+                "title": "Переменные",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "variables"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": true,
+                    "fields": {
+                        "variables": {
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "ui": {
+                                        "options": {
+                                            "collection_icon": "SettingOutlined/FileExcelOutlined",
+                                            "fields": [
+                                                "id",
+                                                "name",
+                                                "value"
+                                            ]
+                                        },
+                                        "list_view": {
+                                            "options": {
+                                                "fields": [
+                                                    "id",
+                                                    "name",
+                                                    "updated_at",
+                                                    "updated_by",
+                                                    "state"
+                                                ],
+                                                "page_size": 50
+                                            }
+                                        }
+                                    },
+                                    "type": "object",
+                                    "params": {
+                                        "inline": false,
+                                        "fields": {
+                                            "id": {
+                                                "title": "Идентификатор переменной",
+                                                "text_search": true,
+                                                "options": {
+                                                    "required": true
+                                                },
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "name": {
+                                                "title": "Название переменной",
+                                                "text_search": true,
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "value": {
+                                                "title": "Значение переменной",
+                                                "type": "string",
+                                                "params": {}
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+            "content": {
+                "title": "Содержимое страницы",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "blocks"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": true,
+                    "fields": {
+                        "blocks": {
+                            "title": "Блоки",
+                            "ui": {
+                                "widget": "BlockList",
+                                "options": {
+                                    "create": {
+                                        "classes": [
+                                            "class_web_blocks"
+                                        ]
+                                    }
+                                }
+                            },
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "title": "Блок",
+                                    "type": "reference",
+                                    "params": {
+                                        "allowedCollections": [
+                                            "web_block_*"
+                                        ]
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+            "seo": {
+                "title": "SEO",
+                "includes": [
+                    {
+                        "ref": "web_seo"
+                    }
+                ],
+                "type": "object",
+                "params": {
+                    "inline": false,
+                    "fields": {}
+                }
+            },
+            "settings": {
+                "title": "Настройки",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "name",
+                            "parent",
+                            "path",
+                            "slug",
+                            "section",
+                            "datasource",
+                            "redirect",
+                            "redirect_path",
+                            "redirect_url",
+                            "outputs",
+                            "navTitle",
+                            "navHide",
+                            "weight"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": true,
+                    "fields": {
+                        "path": {
+                            "title": "Путь",
+                            "unique": true,
+                            "text_search": true,
+                            "options": {
+                                "value": "$perxis.Item.Template == true ? _value : parent == nil ?  '/' : slug == nil ? make_path(parent, slugify(replace_markers(name))) : make_path(parent, slugify(replace_markers(slug)))"
+                            },
+                            "type": "string",
+                            "params": {}
+                        },
+                        "redirect_url": {
+                            "title": "URL для перехода",
+                            "condition": "redirect == true",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "outputs": {
+                            "title": "Форматы страницы для вывода",
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "ui": {
+                                        "widget": "Lookup",
+                                        "options": {
+                                            "allowedCollections": [
+                                                {
+                                                    "collection": "web_outputs"
+                                                }
+                                            ]
+                                        }
+                                    },
+                                    "type": "string",
+                                    "params": {}
+                                }
+                            }
+                        },
+                        "weight": {
+                            "title": "Порядок следования",
+                            "ui": {
+                                "widget": "NumberInput"
+                            },
+                            "type": "number",
+                            "params": {
+                                "format": "int"
+                            }
+                        },
+                        "navHide": {
+                            "title": "Скрыть страницу из навигации",
+                            "ui": {
+                                "widget": "Checkbox"
+                            },
+                            "type": "bool",
+                            "params": {}
+                        },
+                        "section": {
+                            "title": "Раздел",
+                            "ui": {
+                                "widget": "Checkbox"
+                            },
+                            "type": "bool",
+                            "params": {}
+                        },
+                        "parent": {
+                            "title": "Родительский раздел",
+                            "description": "Раздел сайта, где расположена страница",
+                            "ui": {
+                                "widget": "Lookup",
+                                "options": {
+                                    "allowedCollections": [
+                                        {
+                                            "collection": "web_pages"
+                                        }
+                                    ]
+                                }
+                            },
+                            "type": "string",
+                            "params": {}
+                        },
+                        "name": {
+                            "title": "Название",
+                            "indexed": true,
+                            "text_search": true,
+                            "options": {
+                                "required": true,
+                                "value": "$perxis.Item.Template == true ? _value : replace_markers(_value)"
+                            },
+                            "type": "string",
+                            "params": {}
+                        },
+                        "navTitle": {
+                            "title": "Название для навигации",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "redirect_path": {
+                            "title": "Страница для перехода",
+                            "ui": {
+                                "widget": "Lookup",
+                                "options": {
+                                    "allowedCollections": [
+                                        {
+                                            "collection": "web_pages"
+                                        }
+                                    ]
+                                }
+                            },
+                            "condition": "redirect == true",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "datasource": {
+                            "title": "Источник данных",
+                            "description": "Источник данных из которого будут формироваться подстраницы раздела",
+                            "ui": {
+                                "widget": "Lookup",
+                                "options": {
+                                    "allowedCollections": [
+                                        {
+                                            "collection": "web_datasources"
+                                        }
+                                    ]
+                                }
+                            },
+                            "condition": "section == true",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "redirect": {
+                            "title": "Перенаправление",
+                            "description": "Страница не имеет содержимого и перенаправляет посетителей на другой адрес",
+                            "ui": {
+                                "widget": "Checkbox"
+                            },
+                            "type": "bool",
+                            "params": {}
+                        },
+                        "slug": {
+                            "title": "Slug",
+                            "description": "Идентификатор страницы в адресе URL",
+                            "text_search": true,
+                            "options": {
+                                "value": "$perxis.Item.Template == true ? _value : parent == nil ? nil : _value == nil ?  slugify(replace_markers(name)) :  slugify(replace_markers(_value))"
+                            },
+                            "type": "string",
+                            "params": {}
+                        }
+                    }
+                }
+            },
+            "advanced": {
+                "title": "Расширенные настройки",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "scripts"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": false,
+                    "fields": {
+                        "scripts": {
+                            "title": "Дополнительные скрипты",
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "ui": {
+                                        "options": {
+                                            "fields": [
+                                                "src",
+                                                "type",
+                                                "content"
+                                            ]
+                                        }
+                                    },
+                                    "type": "object",
+                                    "params": {
+                                        "inline": false,
+                                        "fields": {
+                                            "type": {
+                                                "title": "Media Type скрипта",
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "content": {
+                                                "title": "Содержимое скрипта",
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "src": {
+                                                "title": "URL для загрузки скрипта",
+                                                "type": "string",
+                                                "params": {}
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    },
+    "loaded": false,
+    "metadata": {
+        "extension": "perxisweb"
+    }
+}
\ No newline at end of file
diff --git a/pkg/schema/test/assets/web_pages.yml b/pkg/schema/test/assets/web_pages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7fdfdd24fa9dcaa04d31ca1506955c86d6e3ce43
--- /dev/null
+++ b/pkg/schema/test/assets/web_pages.yml
@@ -0,0 +1,297 @@
+---
+ui:
+  widget: Tabs
+  options:
+    description: path
+    collection_icon: ApartmentOutlined/FileTextOutlined
+    fields:
+    - content
+    - seo
+    - settings
+    - advanced
+    - design
+    - variables
+    title: name
+    key: path
+  list_view:
+    options:
+      sort:
+      - path
+      page_size: 50
+      fields:
+      - path
+      - name
+      - updated_at
+      - updated_by
+      - state
+includes:
+- ref: hoop_item_options
+  optional: true
+- ref: ext_web_pages_*
+  optional: true
+type: object
+params:
+  inline: false
+  fields:
+    design:
+      title: Дизайн
+      includes:
+      - ref: web_design
+      type: object
+      params:
+        inline: false
+        fields: {}
+    variables:
+      title: Переменные
+      ui:
+        options:
+          fields:
+          - variables
+      type: object
+      params:
+        inline: true
+        fields:
+          variables:
+            type: array
+            params:
+              item:
+                ui:
+                  options:
+                    collection_icon: SettingOutlined/FileExcelOutlined
+                    fields:
+                    - id
+                    - name
+                    - value
+                  list_view:
+                    options:
+                      fields:
+                      - id
+                      - name
+                      - updated_at
+                      - updated_by
+                      - state
+                      page_size: 50
+                type: object
+                params:
+                  inline: false
+                  fields:
+                    id:
+                      title: Идентификатор переменной
+                      text_search: true
+                      options:
+                        required: true
+                      type: string
+                      params: {}
+                    name:
+                      title: Название переменной
+                      text_search: true
+                      type: string
+                      params: {}
+                    value:
+                      title: Значение переменной
+                      type: string
+                      params: {}
+    content:
+      title: Содержимое страницы
+      ui:
+        options:
+          fields:
+          - blocks
+      type: object
+      params:
+        inline: true
+        fields:
+          blocks:
+            title: Блоки
+            ui:
+              widget: BlockList
+              options:
+                create:
+                  classes:
+                  - class_web_blocks
+            type: array
+            params:
+              item:
+                title: Блок
+                type: reference
+                params:
+                  allowedCollections:
+                  - web_block_*
+    seo:
+      title: SEO
+      includes:
+      - ref: web_seo
+      type: object
+      params:
+        inline: false
+        fields: {}
+    settings:
+      title: Настройки
+      ui:
+        options:
+          fields:
+          - name
+          - parent
+          - path
+          - slug
+          - section
+          - datasource
+          - redirect
+          - redirect_path
+          - redirect_url
+          - outputs
+          - navTitle
+          - navHide
+          - weight
+      type: object
+      params:
+        inline: true
+        fields:
+          path:
+            title: Путь
+            unique: true
+            text_search: true
+            options:
+              value: "$perxis.Item.Template == true ? _value : parent == nil ?  '/'
+                : slug == nil ? make_path(parent, slugify(replace_markers(name)))
+                : make_path(parent, slugify(replace_markers(slug)))"
+            type: string
+            params: {}
+          redirect_url:
+            title: URL для перехода
+            condition: redirect == true
+            type: string
+            params: {}
+          outputs:
+            title: Форматы страницы для вывода
+            type: array
+            params:
+              item:
+                ui:
+                  widget: Lookup
+                  options:
+                    allowedCollections:
+                    - collection: web_outputs
+                type: string
+                params: {}
+          weight:
+            title: Порядок следования
+            ui:
+              widget: NumberInput
+            type: number
+            params:
+              format: int
+          navHide:
+            title: Скрыть страницу из навигации
+            ui:
+              widget: Checkbox
+            type: bool
+            params: {}
+          section:
+            title: Раздел
+            ui:
+              widget: Checkbox
+            type: bool
+            params: {}
+          parent:
+            title: Родительский раздел
+            description: Раздел сайта, где расположена страница
+            ui:
+              widget: Lookup
+              options:
+                allowedCollections:
+                - collection: web_pages
+            type: string
+            params: {}
+          name:
+            title: Название
+            indexed: true
+            text_search: true
+            options:
+              required: true
+              value: "$perxis.Item.Template == true ? _value : replace_markers(_value)"
+            type: string
+            params: {}
+          navTitle:
+            title: Название для навигации
+            type: string
+            params: {}
+          redirect_path:
+            title: Страница для перехода
+            ui:
+              widget: Lookup
+              options:
+                allowedCollections:
+                - collection: web_pages
+            condition: redirect == true
+            type: string
+            params: {}
+          datasource:
+            title: Источник данных
+            description: Источник данных из которого будут формироваться подстраницы
+              раздела
+            ui:
+              widget: Lookup
+              options:
+                allowedCollections:
+                - collection: web_datasources
+            condition: section == true
+            type: string
+            params: {}
+          redirect:
+            title: Перенаправление
+            description: Страница не имеет содержимого и перенаправляет посетителей
+              на другой адрес
+            ui:
+              widget: Checkbox
+            type: bool
+            params: {}
+          slug:
+            title: Slug
+            description: Идентификатор страницы в адресе URL
+            text_search: true
+            options:
+              value: "$perxis.Item.Template == true ? _value : parent == nil ? nil
+                : _value == nil ?  slugify(replace_markers(name)) :  slugify(replace_markers(_value))"
+            type: string
+            params: {}
+    advanced:
+      title: Расширенные настройки
+      ui:
+        options:
+          fields:
+          - scripts
+      type: object
+      params:
+        inline: false
+        fields:
+          scripts:
+            title: Дополнительные скрипты
+            type: array
+            params:
+              item:
+                ui:
+                  options:
+                    fields:
+                    - src
+                    - type
+                    - content
+                type: object
+                params:
+                  inline: false
+                  fields:
+                    type:
+                      title: Media Type скрипта
+                      type: string
+                      params: {}
+                    content:
+                      title: Содержимое скрипта
+                      type: string
+                      params: {}
+                    src:
+                      title: URL для загрузки скрипта
+                      type: string
+                      params: {}
+loaded: false
+metadata:
+  extension: perxisweb
diff --git a/pkg/schema/test/convert_test.go b/pkg/schema/test/convert_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5e791570ebfc12c9efc9a4c9295ce5dacced4af5
--- /dev/null
+++ b/pkg/schema/test/convert_test.go
@@ -0,0 +1,174 @@
+package test
+
+import (
+	"os"
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
+	"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/modify"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/validate"
+	"github.com/stretchr/testify/require"
+)
+
+func TestFromFiles(t *testing.T) {
+	t.Run("Non-existen path", func(t *testing.T) {
+		schemas, err := schema.FromFS(os.DirFS("non-existen"))
+		require.Error(t, err)
+		require.ErrorContains(t, err, "no such file or directory")
+		require.Nil(t, schemas)
+	})
+
+	t.Run("Success", func(t *testing.T) {
+		schemas, err := schema.FromFS(os.DirFS("assets"))
+		for _, s := range schemas {
+			s.ClearState()
+		}
+		require.NoError(t, err)
+		require.Len(t, schemas, 2, "В директории хранятся две корректные схемы")
+		require.ElementsMatch(t, []*schema.Schema{getPagesSchema(), getPagesSchema()}, schemas, "Cхемы должны соответствовать объекту из функции")
+	})
+}
+
+// Оригинальное объявление схемы Web/Страницы
+// Значение констант подставлено вручную
+func getPagesSchema() *schema.Schema {
+	content := field.Object(
+		true, "blocks", field.Array(
+			references.Field([]string{"web_block_*"}).SetTitle("Блок"),
+		).
+			SetTitle("Блоки").
+			WithUI(&field.UI{
+				Widget: "BlockList",
+				Options: map[string]interface{}{
+					"create": map[string]interface{}{
+						"classes": []string{"class_web_blocks"},
+					},
+				},
+			}),
+	).SetTitle("Содержимое страницы")
+
+	// SEO
+	seo := field.Object().WithIncludes("web_seo").SetTitle("SEO")
+
+	//Settings
+	settings := field.Object(true,
+		"name", field.String(
+			validate.Required(),
+			modify.Value("$perxis.Item.Template == true ? _value : replace_markers(_value)"),
+		).SetTitle("Название").SetTextSearch(true).SetIndexed(true),
+		"parent", field.String().SetTitle("Родительский раздел").SetDescription("Раздел сайта, где расположена страница").WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_pages"},
+				},
+			},
+		}), // TODO lookup "section == true"
+		"path", field.String(
+			modify.Value("$perxis.Item.Template == true ? _value : parent == nil ?  '/' : slug == nil ? make_path(parent, slugify(replace_markers(name))) : make_path(parent, slugify(replace_markers(slug)))"),
+		).SetTitle("Путь").SetUnique(true).SetTextSearch(true), // TODO readonly
+		"slug", field.String(modify.Value("$perxis.Item.Template == true ? _value : parent == nil ? nil : _value == nil ?  slugify(replace_markers(name)) :  slugify(replace_markers(_value))")).SetTitle("Slug").SetDescription("Идентификатор страницы в адресе URL").SetTextSearch(true),
+		"section", field.Bool().SetTitle("Раздел").WithUI(&field.UI{Widget: "Checkbox"}),
+		"datasource", field.String().WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_datasources"},
+				},
+			},
+		}).SetTitle("Источник данных").SetDescription("Источник данных из которого будут формироваться подстраницы раздела").SetCondition("section == true"),
+		"redirect", field.Bool().SetTitle("Перенаправление").SetDescription("Страница не имеет содержимого и перенаправляет посетителей на другой адрес").WithUI(&field.UI{Widget: "Checkbox"}),
+		"redirect_path", field.String().SetTitle("Страница для перехода").WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_pages"},
+				},
+			},
+		}).SetCondition("redirect == true"),
+		"redirect_url", field.String().SetTitle("URL для перехода").SetCondition("redirect == true"),
+		"outputs", field.Array(field.String().WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_outputs"},
+				},
+			},
+		})).SetTitle("Форматы страницы для вывода"),
+		"navTitle", field.String().SetTitle("Название для навигации"),
+		"navHide", field.Bool().SetTitle("Скрыть страницу из навигации").WithUI(&field.UI{Widget: "Checkbox"}),
+		"weight", field.Number(field.NumberFormatInt).SetTitle("Порядок следования").WithUI(&field.UI{Widget: "NumberInput"}),
+	).SetTitle("Настройки")
+
+	// Advanced
+	advanced := field.Object(
+		"scripts", field.Array(
+			field.Object(
+				"src", field.String().SetTitle("URL для загрузки скрипта"),
+				"type", field.String().SetTitle("Media Type скрипта"),
+				"content", field.String().SetTitle("Содержимое скрипта"),
+			),
+		).SetTitle("Дополнительные скрипты"),
+	).SetTitle("Расширенные настройки")
+
+	// Design
+	design := field.Object().WithIncludes("web_design").SetTitle("Дизайн")
+
+	//Variables
+	variables := field.Object(true, "variables", field.Array(getVarsField())).SetTitle("Переменные")
+
+	// Page
+	page := schema.New(
+		"content", content,
+		"seo", seo,
+		"settings", settings,
+		"advanced", advanced,
+		"design", design,
+		"variables", variables,
+	).WithMetadata(extension.MetadataKey, "perxisweb")
+
+	// Includes
+	page.SetIncludes(
+		field.Include{Ref: "hoop_item_options", Optional: true},
+		field.Include{Ref: "ext_web_pages_*", Optional: true},
+	)
+
+	//UI
+	page.Field.UI.ListView = &field.View{Options: map[string]interface{}{
+		"fields":    []string{"path", "name", "updated_at", "updated_by", "state"},
+		"sort":      []string{"path"},
+		"page_size": 50,
+	}}
+	page.Field.UI.Options["title"] = "name"
+	page.Field.UI.Options["key"] = "path"
+	page.Field.UI.Options["description"] = "path"
+	page.Field.UI.Widget = "Tabs"
+	page.Field.UI.Options["collection_icon"] = "ApartmentOutlined/FileTextOutlined"
+
+	_ = page.ConvertTypes()
+	page.ClearState()
+	return page
+}
+
+func getVarsSchema() (sch *schema.Schema) {
+	sch = schema.New(
+		"id", field.String(validate.Required()).SetTextSearch(true).SetTitle("Идентификатор переменной"),
+		"name", field.String().SetTextSearch(true).SetTitle("Название переменной"),
+		"value", field.String().SetTitle("Значение переменной"),
+	).WithMetadata(extension.MetadataKey, "perxisweb")
+	//UI
+	sch.Field.UI.ListView = &field.View{Options: map[string]interface{}{
+		"fields":    []string{"id", "name", "updated_at", "updated_by", "state"},
+		"page_size": 50,
+	}}
+
+	sch.Field.UI.Options["collection_icon"] = "SettingOutlined/FileExcelOutlined"
+	return sch
+}
+
+func getVarsField() *field.Field {
+	return &getVarsSchema().Field
+}
diff --git a/pkg/schema/test/object_test.go b/pkg/schema/test/object_test.go
index 38868f66989108c142e50eaad62539be381edb4f..8bf3f82022e8501932e6474a985bcfc9f195e693 100644
--- a/pkg/schema/test/object_test.go
+++ b/pkg/schema/test/object_test.go
@@ -65,7 +65,7 @@ func TestNumberField_JSON(t *testing.T) {
 
 	b, err := json.MarshalIndent(fld, "", "  ")
 	require.NoError(t, err)
-	//fmt.Println(string(b))
+	// fmt.Println(string(b))
 
 	res := field.NewField(nil)
 	err = json.Unmarshal(b, res)
@@ -187,12 +187,15 @@ func TestSchema_JSON(t *testing.T) {
 
 	b, err := json.MarshalIndent(sch, "", "  ")
 	require.NoError(t, err)
-	//fmt.Println(string(b))
+	// fmt.Println(string(b))
 
 	res := schema.New()
 	err = json.Unmarshal(b, res)
 	require.NoError(t, err)
 
+	sch.ClearState()
+	res.ClearState()
+
 	assert.Equal(t, sch, res)
 }
 
@@ -213,6 +216,7 @@ func TestSchemaUI_UnmarshalJSON(t *testing.T) {
 		"name", field.String().WithUI(ui),
 	)
 	schm.UI = ui
+	schm.ClearState()
 
 	j := `{
   "ui": {
@@ -288,6 +292,7 @@ func TestSchemaUI_UnmarshalJSON(t *testing.T) {
 
 	sch := schema.New()
 	err := sch.UnmarshalJSON([]byte(j))
+	sch.ClearState()
 	require.NoError(t, err)
 	assert.Equal(t, sch, schm)
 }
@@ -364,7 +369,7 @@ func TestSchema_GetField_WithInline(t *testing.T) {
 			"a", field.String(),
 			"b", field.String(),
 		),
-		"zz", field.Object(
+		"zzz", field.Object(
 			true,
 			"zz", field.Array(field.Object(
 				"str3", field.String(),
@@ -703,7 +708,7 @@ func TestSchema_Modify(t *testing.T) {
 	)
 
 	in := map[string]interface{}{"last_name": "Curie", "name": "Marie"}
-	_, _, err := modify.Modify(nil, sch, in)
+	_, _, err := modify.Modify(context.Background(), sch, in)
 	require.NoError(t, err)
 }
 
@@ -724,7 +729,7 @@ func TestSchema_Validate(t *testing.T) {
 	)
 
 	in := map[string]interface{}{"info": map[string]interface{}{"time": time.Now()}, "name": "Name"}
-	err := validate.Validate(nil, sch, in)
+	err := validate.Validate(context.Background(), sch, in)
 	require.NoError(t, err)
 }
 
@@ -768,9 +773,9 @@ func TestSchema_ValidateErrors(t *testing.T) {
 	)
 
 	in := map[string]interface{}{"a": map[string]interface{}{"num2": 20, "num3": 5, "str1": "123456", "str2": "123", "str3": "some"}}
-	decoded, err := schema.Decode(nil, sch, in)
+	decoded, err := schema.Decode(context.Background(), sch, in)
 	require.NoError(t, err)
-	err = validate.Validate(nil, sch, decoded)
+	err = validate.Validate(context.Background(), sch, decoded)
 	require.Error(t, err)
 	require.Contains(t, err.Error(), "validation error")
 	var merr *multierror.Error
@@ -784,11 +789,11 @@ func TestSchema_ValidateEmptyObject(t *testing.T) {
 			"num1", field.Number(field.NumberFormatInt, validate.Required()),
 		)
 
-		res, err := schema.Decode(nil, sch, nil)
+		res, err := schema.Decode(context.Background(), sch, nil)
 		require.NoError(t, err)
-		res, _, err = modify.Modify(nil, sch, res)
+		res, _, err = modify.Modify(context.Background(), sch, res)
 		require.NoError(t, err)
-		err = validate.Validate(nil, sch, res)
+		err = validate.Validate(context.Background(), sch, res)
 		require.NoError(t, err, "поля объекта nil не проверяются")
 	}
 	{
@@ -796,11 +801,11 @@ func TestSchema_ValidateEmptyObject(t *testing.T) {
 			"num1", field.Number(field.NumberFormatInt, validate.Required()),
 		)
 
-		res, err := schema.Decode(nil, sch, map[string]interface{}{})
+		res, err := schema.Decode(context.Background(), sch, map[string]interface{}{})
 		require.NoError(t, err)
-		res, _, err = modify.Modify(nil, sch, res)
+		res, _, err = modify.Modify(context.Background(), sch, res)
 		require.NoError(t, err)
-		err = validate.Validate(nil, sch, res)
+		err = validate.Validate(context.Background(), sch, res)
 		require.Error(t, err, "поля пустого объекта проверяются")
 	}
 	{
@@ -808,11 +813,11 @@ func TestSchema_ValidateEmptyObject(t *testing.T) {
 			"num1", field.Number(field.NumberFormatInt, validate.Required()),
 		)
 
-		res, err := schema.Decode(nil, sch, map[string]interface{}{"a": "sss"})
+		res, err := schema.Decode(context.Background(), sch, map[string]interface{}{"a": "sss"})
 		require.NoError(t, err)
-		res, _, err = modify.Modify(nil, sch, res)
+		res, _, err = modify.Modify(context.Background(), sch, res)
 		require.NoError(t, err)
-		err = validate.Validate(nil, sch, res)
+		err = validate.Validate(context.Background(), sch, res)
 		require.Error(t, err, "поля объекта с некорректными данными проверяются")
 	}
 
@@ -821,11 +826,11 @@ func TestSchema_ValidateEmptyObject(t *testing.T) {
 			"num1", field.Number(field.NumberFormatInt, validate.Required()),
 		).AddOptions(modify.Default(map[string]interface{}{}))
 
-		res, err := schema.Decode(nil, sch, nil)
+		res, err := schema.Decode(context.Background(), sch, nil)
 		require.NoError(t, err)
-		res, _, err = modify.Modify(nil, sch, res)
+		res, _, err = modify.Modify(context.Background(), sch, res)
 		require.NoError(t, err)
-		err = validate.Validate(nil, sch, res)
+		err = validate.Validate(context.Background(), sch, res)
 		require.Error(t, err, "поля nil объекта Default данными проверяются")
 	}
 }
@@ -839,9 +844,9 @@ func TestSchema_ModificationErrors(t *testing.T) {
 	)
 
 	in := map[string]interface{}{"a": map[string]interface{}{"num1": 20, "num3": 5, "str1": "123456", "str2": "123", "str3": "some"}}
-	decoded, err := schema.Decode(nil, sch, in)
+	decoded, err := schema.Decode(context.Background(), sch, in)
 	require.NoError(t, err)
-	_, _, err = modify.Modify(nil, sch, decoded)
+	_, _, err = modify.Modify(context.Background(), sch, decoded)
 	require.Error(t, err)
 	require.Contains(t, err.Error(), "modification error")
 	var merr *multierror.Error
@@ -857,6 +862,7 @@ func TestSchema_UnknownJSON(t *testing.T) {
 		"times", field.Number("int"),
 		"dates", field.Array(field.Time()),
 	)
+	sch.ClearState()
 
 	b, err := json.Marshal(sch)
 	require.NoError(t, err)
@@ -868,10 +874,10 @@ func TestSchema_UnknownJSON(t *testing.T) {
 	assert.Equal(t, "unknown", s1.GetType().Name(), "Схема неизвестного типа должна определяться как unknown")
 
 	in := map[string]interface{}{"info": map[string]interface{}{"time": time.Now()}, "name": "Name"}
-	out, err := field.Decode(nil, s1, in)
+	out, err := field.Decode(context.Background(), s1, in)
 	require.NoError(t, err)
 	assert.Equal(t, in, out, "Данные неизвестного типа не изменяются при декодировании")
-	err = validate.Validate(nil, s1, in)
+	err = validate.Validate(context.Background(), s1, in)
 	require.NoError(t, err, "Данные неизвестного типа не валидируются вглубь")
 
 	b, err = json.Marshal(s1)
@@ -881,6 +887,8 @@ func TestSchema_UnknownJSON(t *testing.T) {
 	require.NoError(t, err)
 	b, err = json.Marshal(s2)
 	require.NoError(t, err)
+	s1.ClearState()
+	s2.ClearState()
 	assert.Equal(t, "unknown", s2.GetType().Name(), "Схема неизвестного типа должна определяться как unknown")
 	assert.Equal(t, s1, s2, "Схема не должна меняться при повторном маршалинге")
 
@@ -888,6 +896,7 @@ func TestSchema_UnknownJSON(t *testing.T) {
 	s3 := schema.New()
 	err = json.Unmarshal(b, s3)
 	require.NoError(t, err)
+	s3.ClearState()
 	assert.Equal(t, "object", s3.GetType().Name(), "Схема должна восстановить тип object при восстановление регистрации типа")
 	assert.Equal(t, sch, s3, "Схема должна восстановиться при восстановление регистрации типа")
 }
@@ -1343,6 +1352,60 @@ func TestSchema_Introspect(t *testing.T) {
 			[]string{"object_a", "field1", "field2"},
 			false,
 		},
+		{
+			// если у объекта нет данных Introspect возвращает все поля
+			"With not initialized object in data",
+			map[string]interface{}{"object": []interface{}{}},
+			schema.New(
+				"object", field.Array(
+					field.Object(
+						"a", field.String(),
+						"b", field.String(),
+						"c", field.String(),
+					),
+				).WithUI(&field.UI{Widget: "Tags"}),
+			),
+			map[string]interface{}{"object": []interface{}{}},
+			[]string{"object.a", "object.b", "object.c"},
+			[]string{},
+			false,
+		},
+		{
+			// при добавлении значения по умолчанию для поля запрос Introspect возвращает все поля
+			"Object initialized by modify.Default in schema",
+			map[string]interface{}{},
+			schema.New(
+				"object", field.Array(
+					field.Object(
+						"a", field.String(),
+						"b", field.String(),
+						"c", field.String(),
+					),
+				).AddOptions(modify.Default([]interface{}{map[string]interface{}{}})).WithUI(&field.UI{Widget: "Tags"}),
+			),
+			map[string]interface{}{},
+			[]string{"object.a", "object.b", "object.c"},
+			[]string{},
+			false,
+		},
+		{
+			// при добавлении пустого объекта перед запросом Introspect возвращаются все поля
+			"Object initialized in data",
+			map[string]interface{}{"object": []interface{}{map[string]interface{}{}}},
+			schema.New(
+				"object", field.Array(
+					field.Object(
+						"a", field.String(),
+						"b", field.String(),
+						"c", field.String(),
+					),
+				).WithUI(&field.UI{Widget: "Tags"}),
+			),
+			map[string]interface{}{"object": []interface{}{map[string]interface{}{}}},
+			[]string{"object.a", "object.b", "object.c"},
+			[]string{},
+			false,
+		},
 	}
 
 	ctx := context.Background()
@@ -1367,9 +1430,9 @@ func TestSchema_Introspect(t *testing.T) {
 				assert.Nil(t, fld, fmt.Sprintf("поле '%s' должно отсутствовать в схеме", f))
 			}
 
-			//b, err := json.MarshalIndent(got.Schema, "", "  ")
-			//require.NoError(t, err)
-			//fmt.Printf("---\n%s\n---\n", b)
+			// b, err := json.MarshalIndent(got.Schema, "", "  ")
+			// require.NoError(t, err)
+			// fmt.Printf("---\n%s\n---\n", b)
 			assert.Equal(t, tt.want, gotValue)
 		})
 	}
@@ -1472,11 +1535,11 @@ func TestSchema_Load(t *testing.T) {
 	)
 	schema.SetDefaultLoader(loader)
 
-	err := sch.Load(nil)
+	err := sch.Load(context.Background())
 	require.NoError(t, err)
 
-	//b, _ := json.MarshalIndent(sch, "", "  ")
-	//fmt.Println(string(b))
+	// b, _ := json.MarshalIndent(sch, "", "  ")
+	// fmt.Println(string(b))
 
 	assert.NotNil(t, sch.GetField("s1"))
 	assert.NotNil(t, sch.GetField("s2"))
@@ -1525,7 +1588,7 @@ func TestSchema_WithIncludesCircle(t *testing.T) {
 	schema.SetDefaultLoader(loader)
 	sch := schema.NewFromField(field.Object().WithIncludes("f1"))
 
-	err := sch.Load(nil)
+	err := sch.Load(context.Background())
 	require.Error(t, err)
 	assert.EqualError(t, err, "limit for included fields exceeded")
 }
diff --git a/pkg/schema/validate/array_test.go b/pkg/schema/validate/array_test.go
index 57b99445d4681b3994cb9cc5e3c35822a38a4900..ec9e9d818d5b86d7fe9e8647d862e73f44232c65 100644
--- a/pkg/schema/validate/array_test.go
+++ b/pkg/schema/validate/array_test.go
@@ -1,6 +1,7 @@
 package validate
 
 import (
+	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
@@ -28,9 +29,9 @@ func TestArray(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			err = Validate(nil, tt.field, got)
+			err = Validate(context.Background(), tt.field, got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -67,7 +68,7 @@ func TestArrayValidate(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			err := Validate(nil, tt.field, tt.data)
+			err := Validate(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.error)
diff --git a/pkg/schema/validate/enum.go b/pkg/schema/validate/enum.go
index 76d2ecc698792346e1f9dd60940e47d050593b96..8df38c6d68f579f468f0f088170368a31b8f3c80 100644
--- a/pkg/schema/validate/enum.go
+++ b/pkg/schema/validate/enum.go
@@ -25,6 +25,14 @@ func (o EnumOpt) String() string {
 	return fmt.Sprintf("%s", o.Value)
 }
 
+func (o EnumOpt) ValueInt() int {
+	return int(o.Value.(float64))
+}
+
+func (o EnumOpt) ValueString() string {
+	return o.Value.(string)
+}
+
 type enum []EnumOpt
 
 func Enum(opts ...EnumOpt) Validator {
@@ -66,3 +74,12 @@ func (t enum) ValidateOption() error {
 	}
 	return nil
 }
+
+func GetEnum(f *field.Field) []EnumOpt {
+	if v, ok := f.Options["enum"]; ok {
+		if e, ok := v.(*enum); ok {
+			return *e
+		}
+	}
+	return nil
+}
diff --git a/pkg/schema/validate/enum_test.go b/pkg/schema/validate/enum_test.go
index a473dc015f80e92a2b0845228b02023f3f92b63a..a4c301e190dd951da7f0fb3b3c2a2ed014277028 100644
--- a/pkg/schema/validate/enum_test.go
+++ b/pkg/schema/validate/enum_test.go
@@ -1,6 +1,7 @@
 package validate
 
 import (
+	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
@@ -57,9 +58,9 @@ func TestEnum(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			err = Validate(nil, tt.field, got)
+			err = Validate(context.Background(), tt.field, got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/validate/number_test.go b/pkg/schema/validate/number_test.go
index edbd218cf3b19ecd5b412e5a7e74015a8dfaecb5..08f69401807c2cb6da3f403ee20bcbef51c06114 100644
--- a/pkg/schema/validate/number_test.go
+++ b/pkg/schema/validate/number_test.go
@@ -1,6 +1,7 @@
 package validate
 
 import (
+	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
@@ -40,9 +41,9 @@ func TestNumber(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			err = Validate(nil, tt.field, got)
+			err = Validate(context.Background(), tt.field, got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
 				return
@@ -86,7 +87,7 @@ func TestNumberValidate(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			err := Validate(nil, tt.field, tt.data)
+			err := Validate(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.error)
diff --git a/pkg/schema/validate/readonly_test.go b/pkg/schema/validate/readonly_test.go
index ee8ca9ba89a16a02375556d365884bb35d378f83..379d906e139cb1191ef11751cc68ccb10766040f 100644
--- a/pkg/schema/validate/readonly_test.go
+++ b/pkg/schema/validate/readonly_test.go
@@ -1,6 +1,7 @@
 package validate
 
 import (
+	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
@@ -20,9 +21,9 @@ func TestReadonly(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			err = Validate(nil, tt.field, got)
+			err = Validate(context.Background(), tt.field, got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/validate/required_test.go b/pkg/schema/validate/required_test.go
index 41a03ab50d6fab906e7f78229b560e4298475dde..b22a3748fa1a6459d33e76b9114b7dc9a6165351 100644
--- a/pkg/schema/validate/required_test.go
+++ b/pkg/schema/validate/required_test.go
@@ -1,6 +1,7 @@
 package validate
 
 import (
+	"context"
 	"testing"
 	"time"
 
@@ -21,7 +22,7 @@ func TestRequired(t *testing.T) {
 		{"Boolean no value", field.Bool(Required()), nil, true},
 		{"Boolean", field.Bool(Required()), false, false},
 		{"Location: no value", field.Location(Required()), nil, true},
-		//{"Location: empty", field.Location(Required()), map[string]interface{}{}, true}, // не имеет смысла, при Decode вернется ошибка если объект пустой
+		// {"Location: empty", field.Location(Required()), map[string]interface{}{}, true}, // не имеет смысла, при Decode вернется ошибка если объект пустой
 		{"Location: not empty", field.Location(Required()), &field.GeoObject{Address: "addr"}, false},
 		{"Number (int) no value", field.Number(field.NumberFormatInt, Required()), nil, true},
 		{"Number (int) empty", field.Number(field.NumberFormatInt, Required()), 0, false},
@@ -41,9 +42,9 @@ func TestRequired(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			err = Validate(nil, tt.field, got)
+			err = Validate(context.Background(), tt.field, got)
 			if (err != nil) != tt.wantErr {
 				t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
 				return
diff --git a/pkg/schema/validate/string_test.go b/pkg/schema/validate/string_test.go
index 9d48cf4d75c5c7bba2763c125bc1b598e78b23ac..0fac9bb7c83d4a49b2b834cd779c74f3be529230 100644
--- a/pkg/schema/validate/string_test.go
+++ b/pkg/schema/validate/string_test.go
@@ -1,6 +1,7 @@
 package validate
 
 import (
+	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
@@ -112,9 +113,9 @@ func TestString(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := field.Decode(nil, tt.field, tt.data)
+			got, err := field.Decode(context.Background(), tt.field, tt.data)
 			require.NoError(t, err)
-			err = Validate(nil, tt.field, got)
+			err = Validate(context.Background(), tt.field, got)
 			if tt.wantErr {
 				require.Error(t, err)
 			}
@@ -151,7 +152,7 @@ func TestStringValidate(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			err := Validate(nil, tt.field, tt.data)
+			err := Validate(context.Background(), tt.field, tt.data)
 			if tt.wantErr {
 				require.Error(t, err)
 				assert.EqualError(t, err, tt.error)
diff --git a/pkg/schema/walk/fn.go b/pkg/schema/walk/fn.go
index 9b1d4f22316cf37cae1af824d98f611b7dc0bcf7..a4157783278318096cd5c3a9ee8a67ab388223a8 100644
--- a/pkg/schema/walk/fn.go
+++ b/pkg/schema/walk/fn.go
@@ -14,3 +14,13 @@ func KeepSrc(c *WalkContext) (err error) {
 	c.Changed = true
 	return
 }
+
+func RemoveValue(c *WalkContext) (err error) {
+	if c.Dst == nil {
+		return
+	}
+
+	c.Dst = nil
+	c.Changed = true
+	return
+}
diff --git a/pkg/schema/walk/walk.go b/pkg/schema/walk/walk.go
index 4757742f82efcaffd5faba47eb076ed727783c75..ab11ebe6ee0614a702c4c2a0f5747afe0e994ea4 100644
--- a/pkg/schema/walk/walk.go
+++ b/pkg/schema/walk/walk.go
@@ -93,7 +93,7 @@ func (m *Walker) datawalk(w *WalkContext) (err error) {
 	path := w.GetPath()
 	fn := m.DefaultFn
 
-	fieldCfg, _ := m.config.Fields[path]
+	fieldCfg := m.config.Fields[path]
 
 	if fieldCfg.Fn != nil {
 		fn = fieldCfg.Fn
@@ -120,6 +120,7 @@ func (m *Walker) datawalk(w *WalkContext) (err error) {
 
 		fields := p.GetFields(true)
 
+		var objChanged bool
 		for k := range keys {
 			f, ok := fields[k]
 			if !ok {
@@ -144,13 +145,16 @@ func (m *Walker) datawalk(w *WalkContext) (err error) {
 			}
 
 			if wc.Changed {
+				objChanged = true
 				w.Changed = true
 			}
 		}
-		if len(res) > 0 {
+
+		if objChanged || len(res) != len(d) { // in generic merge unknown fields are removed but change is not set
 			w.Dst = res
 		}
 
+
 	case *field.ArrayParameters:
 		d, _ := w.Dst.([]interface{})
 		s, _ := w.Src.([]interface{})
@@ -175,7 +179,6 @@ func (m *Walker) datawalk(w *WalkContext) (err error) {
 				w.Changed = true
 			}
 		}
-
 	}
 
 	return
diff --git a/pkg/schema/walk/walk_test.go b/pkg/schema/walk/walk_test.go
index 3fd01bd577c3fe16abb1cae7b2e9785e0edc659c..be33aa2fed74abb6493cd9aa5dd2b3470847c3d8 100644
--- a/pkg/schema/walk/walk_test.go
+++ b/pkg/schema/walk/walk_test.go
@@ -56,10 +56,10 @@ func TestWalker_DataWalk(t *testing.T) {
 					"a": "src_obj1_a",
 					"b": "src_obj1_b",
 					"obj2": map[string]interface{}{
-						"a": "dst_obj1_obj2_a",
+						"a": "src_obj1_obj2_a",
 					},
 					"obj3": map[string]interface{}{
-						"e": "dst_obj1_obj3_e",
+						"e": "src_obj1_obj3_e",
 					},
 				},
 				"inline_str_1": "src_inline_1",
@@ -109,10 +109,10 @@ func TestWalker_DataWalk(t *testing.T) {
 					"a": "src_obj1_a",
 					"b": "src_obj1_b",
 					"obj2": map[string]interface{}{
-						"a": "dst_obj1_obj2_a",
+						"a": "src_obj1_obj2_a",
 					},
 					"obj3": map[string]interface{}{
-						"e": "dst_obj1_obj3_e",
+						"e": "src_obj1_obj3_e",
 					},
 				},
 				"inline_str_1": "src_inline_1",
@@ -162,10 +162,10 @@ func TestWalker_DataWalk(t *testing.T) {
 					"a": "src_obj1_a",
 					"b": "src_obj1_b",
 					"obj2": map[string]interface{}{
-						"a": "dst_obj1_obj2_a",
+						"a": "src_obj1_obj2_a",
 					},
 					"obj3": map[string]interface{}{
-						"e": "dst_obj1_obj3_e",
+						"e": "src_obj1_obj3_e",
 					},
 				},
 				"inline_str_1": "src_inline_1",
@@ -179,10 +179,10 @@ func TestWalker_DataWalk(t *testing.T) {
 					"a": "src_obj1_a",
 					"b": "src_obj1_b",
 					"obj2": map[string]interface{}{
-						"a": "dst_obj1_obj2_a",
+						"a": "src_obj1_obj2_a",
 					},
 					"obj3": map[string]interface{}{
-						"e": "dst_obj1_obj3_e",
+						"e": "src_obj1_obj3_e",
 					},
 				},
 				"inline_str_1": "src_inline_1",
@@ -196,10 +196,10 @@ func TestWalker_DataWalk(t *testing.T) {
 					"a": "src_obj1_a",
 					"b": "src_obj1_b",
 					"obj2": map[string]interface{}{
-						"a": "dst_obj1_obj2_a",
+						"a": "src_obj1_obj2_a",
 					},
 					"obj3": map[string]interface{}{
-						"e": "dst_obj1_obj3_e",
+						"e": "src_obj1_obj3_e",
 					},
 				},
 				"inline_str_1": "src_inline_1",
@@ -208,6 +208,220 @@ func TestWalker_DataWalk(t *testing.T) {
 			},
 			false, false,
 		},
+		{"keep src nil",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"a":            {Fn: KeepSrc},
+					"obj1.a":       {Fn: KeepSrc},
+					"inline_str_1": {Fn: KeepSrc},
+					"slice.1":      {Fn: KeepSrc},
+				},
+			},
+			map[string]interface{}{
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "scr_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "src_obj1_obj3_e",
+					},
+				},
+				"slice": []interface{}{"src_s1"},
+			},
+			map[string]interface{}{
+				"a": "dst_a",
+				"b": "dst_b",
+				"obj1": map[string]interface{}{
+					"a": "dst_obj1_a",
+					"b": "dst_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"dst_s1", "dst_s2"},
+			},
+			map[string]interface{}{
+				"b": "dst_b",
+				"obj1": map[string]interface{}{
+					"b": "dst_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"dst_s1", nil},
+			},
+			true, false,
+		},
+		{"keep src nil #2",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"a": {Fn: KeepSrc},
+				},
+			},
+			map[string]interface{}{},
+			map[string]interface{}{
+				"a": "dst_a",
+			},
+			map[string]interface{}{},
+			true, false,
+		},
+		{"keep src ni #3",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"obj1":  {Fn: KeepSrc},
+					"slice": {Fn: KeepSrc},
+				},
+			},
+			map[string]interface{}{
+				"b": "src_b",
+			},
+			map[string]interface{}{
+				"a": "dst_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"src_s1", "src_s2"},
+			},
+			map[string]interface{}{
+				"a":            "dst_a",
+				"b":            "src_b",
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+			},
+			true, false,
+		},
+		{"keep src ni #3",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"obj1.a":    {Fn: KeepSrc},
+					"obj1.b":    {Fn: KeepSrc},
+					"obj1.obj2": {Fn: KeepSrc},
+					"obj1.obj3": {Fn: KeepSrc},
+				},
+			},
+			map[string]interface{}{
+				"b": "src_b",
+			},
+			map[string]interface{}{
+				"a": "dst_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+			},
+			map[string]interface{}{
+				"a":    "dst_a",
+				"b":    "src_b",
+				"obj1": map[string]interface{}{},
+			},
+			true, false,
+		},
+		{"remove fields",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"a":            {Fn: RemoveValue},
+					"obj1.a":       {Fn: RemoveValue},
+					"inline_str_1": {Fn: RemoveValue},
+					"slice.1":      {Fn: RemoveValue},
+				},
+			},
+			map[string]interface{}{},
+			map[string]interface{}{
+				"a": "dst_a",
+				"b": "dst_b",
+				"obj1": map[string]interface{}{
+					"a": "dst_obj1_a",
+					"b": "dst_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"dst_s1", "dts_s2"},
+			},
+			map[string]interface{}{
+				"b": "dst_b",
+				"obj1": map[string]interface{}{
+					"b": "dst_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"dst_s1", nil},
+			},
+			true, false,
+		},
+		{"remove fields #2",
+			&WalkConfig{
+				Fields: map[string]FieldConfig{
+					"obj1":  {Fn: RemoveValue},
+					"slice": {Fn: RemoveValue},
+				},
+			},
+			map[string]interface{}{
+				"b": "src_b",
+			},
+			map[string]interface{}{
+				"a": "dst_a",
+				"b": "src_b",
+				"obj1": map[string]interface{}{
+					"a": "src_obj1_a",
+					"b": "src_obj1_b",
+					"obj2": map[string]interface{}{
+						"a": "dst_obj1_obj2_a",
+					},
+					"obj3": map[string]interface{}{
+						"e": "dst_obj1_obj3_e",
+					},
+				},
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+				"slice":        []interface{}{"src_s1", "src_s2"},
+			},
+			map[string]interface{}{
+				"a":            "dst_a",
+				"b":            "src_b",
+				"inline_str_1": "dst_inline_1",
+				"inline_str_2": "dst_inline_2",
+			},
+			true, false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/schemaloader/loader_test.go b/pkg/schemaloader/loader_test.go
index f796fd8f669e31140e4171955c4d7db8723cec26..b7cc32b21a91c2ad6efd86c9918800e56496919e 100644
--- a/pkg/schemaloader/loader_test.go
+++ b/pkg/schemaloader/loader_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-//func Test_Load(t *testing.T) {
+// func Test_Load(t *testing.T) {
 //
 //	const (
 //		spaceID = "SpaceID"
@@ -107,10 +107,10 @@ import (
 //		require.Nil(t, schemas, "Метод должен вернуть nil")
 //		collSvs.AssertExpectations(t)
 //	})
-//}
+// }
 
 func Test_parseRef(t *testing.T) {
-	ctx := WithContext(nil, &LoaderContext{SpaceID: "spc", EnvID: "env"})
+	ctx := WithContext(context.Background(), &LoaderContext{SpaceID: "spc", EnvID: "env"})
 	tests := []struct {
 		ref            string
 		ctx            context.Context
diff --git a/pkg/setup/client.go b/pkg/setup/client.go
index cc103faf1dd4dca2ee1547e88b150819ebb730a9..c40ad506fb1e2bee225cf6acd2930d7364bbb8b0 100644
--- a/pkg/setup/client.go
+++ b/pkg/setup/client.go
@@ -4,8 +4,9 @@ import (
 	"context"
 	"strings"
 
-	"git.perx.ru/perxis/perxis-go/pkg/clients"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
 	"go.uber.org/zap"
 )
 
@@ -15,56 +16,32 @@ var (
 	ErrUninstallClients = errors.New("failed to uninstall clients")
 )
 
-type ClientsOption func(c *ClientConfig)
-type UpdateClientFn func(s *Setup, exist, new *clients.Client) (*clients.Client, bool)
-type DeleteClientFn func(s *Setup, client *clients.Client) bool
-
-type ClientConfig struct {
-	client   *clients.Client
-	UpdateFn UpdateClientFn
-	DeleteFn DeleteClientFn
-}
-
-func NewClientConfig(client *clients.Client, opt ...ClientsOption) ClientConfig {
-	c := ClientConfig{client: client}
-
-	UpdateExistingClient()(&c)
-	DeleteClientIfRemove()(&c)
-
-	for _, o := range opt {
-		o(&c)
-	}
-
-	return c
-}
-
-func OverwriteClient() ClientsOption {
-	return func(c *ClientConfig) {
-		c.UpdateFn = func(s *Setup, old, new *clients.Client) (*clients.Client, bool) { return new, true }
-	}
-}
+type (
+	Client       = Entity[ClientConf, *clients.Client]
+	Clients      = EntityList[ClientConf, *clients.Client]
+	ClientOption = EntityOption[ClientConf, *clients.Client]
+	ClientConf   struct{}
+)
 
-func KeepExistingClient() ClientsOption {
-	return func(c *ClientConfig) {
-		c.UpdateFn = func(s *Setup, old, new *clients.Client) (*clients.Client, bool) { return old, false }
-	}
+func (ClientConf) Init(e *Entity[ClientConf, *clients.Client]) {
+	UpdateExistingClient()(e)
+	DeleteClientIfRemoveFlag()(e)
 }
 
-func DeleteClient() ClientsOption {
-	return func(c *ClientConfig) {
-		c.DeleteFn = func(s *Setup, client *clients.Client) bool { return true }
-	}
-}
+var (
+	OverwriteClient          = Overwrite[ClientConf, *clients.Client]
+	KeepExistingClient       = Keep[ClientConf, *clients.Client]
+	DeleteClient             = Delete[ClientConf, *clients.Client]
+	DeleteClientIfRemoveFlag = DeleteIfRemoveFlag[ClientConf, *clients.Client]
+)
 
-func DeleteClientIfRemove() ClientsOption {
-	return func(c *ClientConfig) {
-		c.DeleteFn = func(s *Setup, client *clients.Client) bool { return s.IsRemove() }
-	}
-}
+/////
 
-func UpdateExistingClient() ClientsOption {
-	return func(c *ClientConfig) {
-		c.UpdateFn = func(s *Setup, exist, client *clients.Client) (*clients.Client, bool) {
+// UpdateExistingClient обновляет существующий клиент, если он существует
+// и не содержит необходимых данных
+func UpdateExistingClient() ClientOption {
+	return func(c *Client) {
+		c.UpdateFunc = func(s *Setup, exist, client *clients.Client) (*clients.Client, bool) {
 			if exist.Name == "" {
 				exist.Name = client.Name
 			}
@@ -93,61 +70,90 @@ func UpdateExistingClient() ClientsOption {
 	}
 }
 
-func (s *Setup) InstallClients(ctx context.Context) error {
-	if len(s.Clients) == 0 {
-		return nil
-	}
+//
+// Client Setup
+//
 
-	s.logger.Debug("Install clients", zap.String("Space ID", s.SpaceID))
+// AddClient добавляет требования к настройке элементов в пространстве
+func (s *Setup) AddClient(client *clients.Client, opt ...ClientOption) *Setup {
+	s.Config.Clients.Add(client, opt...)
+	return s
+}
 
-	for _, c := range s.Clients {
-		err := s.InstallClient(ctx, c)
-		if err != nil {
-			s.logger.Error("Failed to install client", zap.String("Client ID", c.client.ID), zap.String("Client Name", c.client.Name), zap.Error(err))
-			return errors.WithDetailf(errors.Wrap(err, "failed to install client"), "Возникла ошибка при настройке клиента %s(%s)", c.client.Name, c.client.ID)
-		}
-	}
-	return nil
+// AddClients добавляет требования к настройке элементов в пространстве
+func (s *Setup) AddClients(clients []*clients.Client, opt ...ClientOption) *Setup {
+	s.Config.Clients.AddMany(clients, opt...)
+	return s
 }
 
-func (s *Setup) InstallClient(ctx context.Context, c ClientConfig) error {
-	client := c.client
+// InstallClient устанавливает клиент в пространстве
+func (s *Setup) InstallClient(ctx context.Context, c *Client) error {
+	client := c.Value(s)
 	client.SpaceID = s.SpaceID
 
 	if s.IsForce() {
-		s.content.Clients.Delete(ctx, s.SpaceID, c.client.ID)
-		_, err := s.content.Clients.Create(ctx, c.client)
+		_ = s.content.Clients.Delete(ctx, s.SpaceID, client.ID)
+		_, err := s.content.Clients.Create(ctx, client)
 		return err
 	}
 
-	exist, err := s.content.Clients.Get(ctx, s.SpaceID, c.client.ID)
+	exist, err := s.content.Clients.Get(ctx, s.SpaceID, client.ID)
 	if err != nil {
 		if !strings.Contains(err.Error(), clients.ErrNotFound.Error()) {
 			return err
 		}
 
-		_, err = s.content.Clients.Create(ctx, c.client)
+		_, err = s.content.Clients.Create(ctx, client)
 		return err
 	}
 
-	if client, upd := c.UpdateFn(s, exist, c.client); upd {
+	if client, upd := c.UpdateFunc(s, exist, client); upd {
 		return s.content.Clients.Update(ctx, client)
 	}
 
 	return nil
 }
 
+// InstallClients устанавливает все клиенты в пространстве
+func (s *Setup) InstallClients(ctx context.Context) error {
+	if len(s.Clients) == 0 {
+		return nil
+	}
+
+	s.logger.Debug("Install clients", zap.String("Space ID", s.SpaceID))
+
+	for _, c := range s.Clients {
+		err := s.InstallClient(ctx, c)
+		if err != nil {
+			client := c.Value(s)
+			s.logger.Error("Failed to install client", zap.String("Client ID", client.ID), zap.String("Client Name", client.Name), zap.Error(err))
+			return errors.WithDetailf(errors.Wrap(err, "failed to install client"), "Возникла ошибка при настройке клиента %s(%s)", client.Name, client.ID)
+		}
+	}
+	return nil
+}
+
+// CheckClient проверяет наличие клиента в пространстве
+func (s *Setup) CheckClient(ctx context.Context, c *Client) error {
+	client := c.Value(s)
+	_, err := s.content.Clients.Get(ctx, s.SpaceID, client.ID)
+	return err
+}
+
+// CheckClients проверяет наличие всех клиентов в пространстве
 func (s *Setup) CheckClients(ctx context.Context) (err error) {
 	if len(s.Clients) == 0 {
 		return nil
 	}
 
-	var errs []error
 	s.logger.Debug("Check clients", zap.String("Space ID", s.SpaceID))
+
+	var errs []error
 	for _, c := range s.Clients {
-		err := s.CheckClient(ctx, c.client)
+		err := s.CheckClient(ctx, c)
 		if err != nil {
-			errs = append(errs, errors.WithDetailf(err, "Не найден клиент %s(%s)", c.client.Name, c.client.ID))
+			client := c.Value(s)
+			errs = append(errs, errors.WithDetailf(err, "Не найден клиент %s(%s)", client.Name, client.ID))
 		}
 	}
 
@@ -158,11 +164,18 @@ func (s *Setup) CheckClients(ctx context.Context) (err error) {
 	return nil
 }
 
-func (s *Setup) CheckClient(ctx context.Context, client *clients.Client) error {
-	_, err := s.content.Clients.Get(ctx, s.SpaceID, client.ID)
-	return err
+// UninstallClient удаляет клиент из пространства
+func (s *Setup) UninstallClient(ctx context.Context, c *Client) error {
+	client := c.Value(s)
+	if c.DeleteFunc(s, client) {
+		if err := s.content.Clients.Delete(ctx, s.SpaceID, client.ID); err != nil && !strings.Contains(err.Error(), clients.ErrNotFound.Error()) {
+			return err
+		}
+	}
+	return nil
 }
 
+// UninstallClients удаляет все клиенты из пространства
 func (s *Setup) UninstallClients(ctx context.Context) error {
 	if len(s.Clients) == 0 {
 		return nil
@@ -172,19 +185,13 @@ func (s *Setup) UninstallClients(ctx context.Context) error {
 
 	for _, c := range s.Clients {
 		if err := s.UninstallClient(ctx, c); err != nil {
-			s.logger.Error("Failed to uninstall client", zap.String("Client ID", c.client.ID), zap.String("Client Name", c.client.Name), zap.Error(err))
-			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall client"), "Возникла ошибка при удалении клиента %s(%s)", c.client.Name, c.client.ID)
+			client := c.Value(s)
+			s.logger.Error("Failed to uninstall client", zap.String("Client ID", client.ID),
+				zap.String("Client Name", client.Name), zap.Error(err))
+			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall client"),
+				"Возникла ошибка при удалении клиента %s(%s)", client.Name, client.ID)
 		}
 	}
 
 	return nil
 }
-
-func (s *Setup) UninstallClient(ctx context.Context, c ClientConfig) error {
-	if c.DeleteFn(s, c.client) {
-		if err := s.content.Clients.Delete(ctx, s.SpaceID, c.client.ID); err != nil && !strings.Contains(err.Error(), clients.ErrNotFound.Error()) {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/pkg/setup/client_test.go b/pkg/setup/client_test.go
index d345756a2b0b87e7ad10aebc1ee9ce0899df199c..6626ec18429955e063139fa24f729ac6d9476dcc 100644
--- a/pkg/setup/client_test.go
+++ b/pkg/setup/client_test.go
@@ -1,12 +1,10 @@
 package setup
 
 import (
-	"context"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/clients"
 	clientsMock "git.perx.ru/perxis/perxis-go/pkg/clients/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
@@ -89,9 +87,9 @@ func TestSetup_InstallClients(t *testing.T) {
 				tt.clientsCall(c)
 			}
 
-			s := NewSetup(&content.Content{Clients: c}, "sp", "env", nil)
-			s.AddClients(tt.clients)
-			tt.wantErr(t, s.InstallClients(context.Background()))
+			//s := NewSetup(&content.Content{Clients: c}, "sp", "env", nil)
+			//s.AddClients(tt.clients)
+			//tt.wantErr(t, s.InstallClients(context.Background()))
 		})
 	}
 }
diff --git a/pkg/setup/collection.go b/pkg/setup/collection.go
index 9f5f6a46c7818d54db7e5123cdbef595b0cc0ab9..e597acfcabefd70a9a8abb3d2fed64c11fb274d2 100644
--- a/pkg/setup/collection.go
+++ b/pkg/setup/collection.go
@@ -4,8 +4,9 @@ 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/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"go.uber.org/zap"
 )
@@ -16,106 +17,145 @@ var (
 	ErrUninstallCollections = errors.New("failed to uninstall collections")
 )
 
-type CollectionsOption func(c *CollectionConfig)
-type UpdateCollectionFn func(s *Setup, exist, new *collections.Collection) (coll *collections.Collection, upd bool, setSchema bool, err error)
-type DeleteCollectionFn func(s *Setup, col *collections.Collection) (bool, error)
+//
+// Collection Configuration
+//
 
-type CollectionConfig struct {
-	collection    *collections.Collection
-	UpdateFn      UpdateCollectionFn
-	DeleteFn      DeleteCollectionFn
-	SkipMigration bool
-}
+type (
+	Collection       = Entity[CollectionConf, *collections.Collection]
+	Collections      = EntityList[CollectionConf, *collections.Collection]
+	CollectionOption = EntityOption[CollectionConf, *collections.Collection]
+	CollectionFilter = FilterFunc[*collections.Collection]
+
+	CollectionConf struct {
+		SkipMigration bool
+	}
+)
 
-func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsOption) (c CollectionConfig, err error) {
-	collection = collection.Clone()
+var (
+	OverwriteCollection          = Overwrite[CollectionConf, *collections.Collection]
+	KeepCollection               = Keep[CollectionConf, *collections.Collection]
+	DeleteCollection             = Delete[CollectionConf, *collections.Collection]
+	DeleteCollectionIfRemoveFlag = DeleteIfRemoveFlag[CollectionConf, *collections.Collection]
+)
 
-	if collection.Schema != nil {
-		// приведение внутренних типов схемы, чтобы избежать возможного несоответствия типов при
-		// сравнивании схем (`[]interface{}/[]string`, `int/int64`, etc.)
-		err = collection.Schema.ConvertTypes()
-		if err != nil {
-			return
+// Init инициализирует конфигурацию коллекции
+func (CollectionConf) Init(e *Entity[CollectionConf, *collections.Collection]) {
+	// Сформированная вручную схема может иметь разные типы данных для одних и тех же полей
+	// (`[]interface{}/[]string`, `int/int64`, etc.), что может привести к ошибкам
+	// при сравнении схем. Поэтому приводим все типы данных к одному типу.
+	if e.value.Schema != nil {
+		if err := e.value.Schema.ConvertTypes(); err != nil {
+			panic(err)
 		}
 	}
 
-	c = CollectionConfig{collection: collection}
+	// Устанавливаем стратегии обновления и удаления коллекции по умолчанию
+	UpdateExistingCollection()(e)
+	DeleteCollectionIfRemoveFlag()(e)
+}
 
-	DefaultUpdateCollectionStrategy()(&c)
-	DeleteCollectionIfRemove()(&c)
+func IsSchemaUpdateRequired(old, new *collections.Collection) bool {
+	return !new.IsView() && (old == nil || !old.Schema.Equal(new.Schema))
+}
 
-	for _, o := range opt {
-		o(&c)
+// AddMetadata добавляет метаданные к коллекции
+func AddMetadata(key, value string) CollectionOption {
+	return func(e *Collection) {
+		e.ValueFunc = append(e.ValueFunc, func(s *Setup, c *collections.Collection) {
+			if c.Schema != nil {
+				c.Schema.WithMetadata(key, value)
+			}
+		})
 	}
-
-	return c, nil
 }
 
-func SkipMigration() CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.SkipMigration = true
+// SkipMigration пропускает миграцию коллекции
+func SkipMigration() CollectionOption {
+	return func(e *Collection) {
+		e.Conf.SkipMigration = true
 	}
 }
 
-func OverwriteCollection() CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.UpdateFn = func(s *Setup, old, new *collections.Collection) (*collections.Collection, bool, bool, error) {
-			update := new.Name != old.Name || new.IsSingle() != old.IsSingle() || new.IsSystem() != old.IsSystem() ||
-				new.IsNoData() != old.IsNoData() || new.Hidden != old.Hidden || !new.View.Equal(old.View) || !data.ElementsMatch(old.Tags, new.Tags)
+// UpdateExistingCollection обновляет существующую коллекцию
+func UpdateExistingCollection() CollectionOption {
+	return func(e *Collection) {
+		e.UpdateFunc = func(s *Setup, old, new *collections.Collection) (*collections.Collection, bool) {
+			// Копируем теги из старой коллекции в новую
+			if len(old.Tags) > 0 {
+				new.Tags = data.SetFromSlice(append(old.Tags, new.Tags...))
+			}
 
-			return new, update, !old.Schema.Equal(new.Schema), nil
-		}
-	}
-}
+			var update bool
+			update = new.Name != old.Name || new.IsSingle() != old.IsSingle() || new.IsSystem() != old.IsSystem() ||
+				new.IsNoData() != old.IsNoData() || new.Hidden != old.Hidden || new.IsView() != old.IsView() && data.ElementsMatch(old.Tags, new.Tags)
+
+			if old.View != nil && new.View != nil {
+				update = update || *old.View != *new.View
+			}
 
-func KeepExistingCollection() CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.UpdateFn = func(s *Setup, old, new *collections.Collection) (*collections.Collection, bool, bool, error) {
-			return old, false, false, nil
+			return new, update || IsSchemaUpdateRequired(old, new)
 		}
 	}
 }
 
-func DeleteCollection() CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.DeleteFn = func(s *Setup, collection *collections.Collection) (bool, error) { return true, nil }
-	}
-}
+//
+// Collection Setup
+//
 
-func DeleteCollectionIfRemove() CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.DeleteFn = func(s *Setup, collection *collections.Collection) (bool, error) { return s.IsRemove(), nil }
-	}
+// AddCollection добавляет требование к настройке коллекции в пространстве (runtime)
+func (s *Setup) AddCollection(collection *collections.Collection, opt ...CollectionOption) *Setup {
+	s.Config.Collections.Add(collection, opt...)
+	return s
 }
 
-func DefaultUpdateCollectionStrategyFn(_ *Setup, exist, collection *collections.Collection) (*collections.Collection, bool, bool, error) {
-	if len(exist.Tags) > 0 {
-		collection.Tags = data.SetFromSlice(append(exist.Tags, collection.Tags...))
+// AddCollections добавляет несколько требований к настройке коллекций в пространстве (runtime)
+func (s *Setup) AddCollections(collections []*collections.Collection, opt ...CollectionOption) *Setup {
+	for _, c := range collections {
+		s.AddCollection(c, opt...)
 	}
+	return s
+}
 
-	var update, setSchema bool
-	update = collection.Name != exist.Name || collection.IsSingle() != exist.IsSingle() || collection.IsSystem() != exist.IsSystem() ||
-		collection.IsNoData() != exist.IsNoData() || collection.Hidden != exist.Hidden || collection.IsView() != exist.IsView() && data.ElementsMatch(exist.Tags, collection.Tags)
+func (s *Setup) InstallCollection(ctx context.Context, c *Collection) (updateSchema bool, err error) {
+	collection := c.Value(s)
+	collection.SpaceID, collection.EnvID = s.SpaceID, s.EnvironmentID
 
-	if exist.View != nil && collection.View != nil {
-		update = update && *exist.View == *collection.View
+	var exist *collections.Collection
+	// isForce - не удалять коллекцию, если она уже существует
+	exist, err = s.content.Collections.Get(ctx, collection.SpaceID, collection.EnvID, collection.ID)
+	if err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
+		return false, err
 	}
 
-	setSchema = !collection.IsView() && !exist.Schema.Equal(collection.Schema)
-
-	return collection, update, setSchema, nil
-}
+	if exist == nil {
+		if _, err = s.content.Collections.Create(ctx, collection); err != nil {
+			return false, err
+		}
+	} else {
+		var updateCollection bool
+		collection, updateCollection = c.UpdateFunc(s, exist, collection)
+		if !updateCollection {
+			return false, nil
+		}
 
-func DefaultUpdateCollectionStrategy() CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.UpdateFn = DefaultUpdateCollectionStrategyFn
+		// TODO: Проверить, что коллекция изменилась
+		// TODO: Тест на сравнение схем
+		// Замена возможного алиаса окружения на реального ID окружения перед сравнением
+		collection.EnvID = exist.EnvID
+		if !exist.Equal(collection) {
+			if err = s.content.Collections.Update(ctx, collection); err != nil {
+				return false, err
+			}
+		}
 	}
-}
 
-func WithUpdateCollectionStrategy(fn UpdateCollectionFn) CollectionsOption {
-	return func(c *CollectionConfig) {
-		c.UpdateFn = fn
+	// Проверяем, нужно ли обновить схему коллекции
+	if IsSchemaUpdateRequired(exist, collection) {
+		return true, s.content.Collections.SetSchema(ctx, collection.SpaceID, collection.EnvID, collection.ID, collection.Schema)
 	}
+
+	return false, nil
 }
 
 func (s *Setup) InstallCollections(ctx context.Context) (err error) {
@@ -130,14 +170,16 @@ func (s *Setup) InstallCollections(ctx context.Context) (err error) {
 	for _, c := range s.Collections {
 		setSchema, err = s.InstallCollection(ctx, c)
 		if err != nil {
+			collection := c.Value(s)
 			s.logger.Error("Failed to install collection",
-				zap.String("Collection ID", c.collection.ID),
-				zap.String("Collection Name", c.collection.Name),
+				zap.String("Collection ID", collection.ID),
+				zap.String("Collection Name", collection.Name),
 				zap.Error(err),
 			)
-			return errors.WithDetailf(errors.Wrap(err, "failed to install collection"), "Возникла ошибка при настройке коллекции %s(%s)", c.collection.Name, c.collection.ID)
+			return errors.WithDetailf(errors.Wrap(err, "failed to install collection"), "Возникла ошибка при настройке коллекции %s(%s)", collection.Name, collection.ID)
 		}
-		if !c.SkipMigration && setSchema {
+
+		if !c.Conf.SkipMigration && setSchema {
 			migrate = true
 		}
 	}
@@ -158,46 +200,6 @@ func (s *Setup) InstallCollections(ctx context.Context) (err error) {
 	return nil
 }
 
-func (s *Setup) InstallCollection(ctx context.Context, c CollectionConfig) (setSchema bool, err error) {
-	collection := c.collection
-	collection.SpaceID, collection.EnvID = s.SpaceID, s.EnvironmentID
-
-	var exist *collections.Collection
-	// isForce - не удалять коллекцию, если она уже существует
-	exist, err = s.content.Collections.Get(ctx, collection.SpaceID, collection.EnvID, collection.ID)
-	if err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
-		return false, err
-	}
-
-	if exist == nil {
-		setSchema = !collection.IsView()
-		_, err = s.content.Collections.Create(ctx, collection)
-		if err != nil {
-			return false, err
-		}
-	} else {
-		var upd bool
-		collection, upd, setSchema, err = c.UpdateFn(s, exist, c.collection)
-		if err != nil {
-			return false, err
-		}
-		if upd {
-			if err = s.content.Collections.Update(ctx, collection); err != nil {
-				return false, err
-			}
-		}
-	}
-
-	if setSchema {
-		err = s.content.Collections.SetSchema(ctx, collection.SpaceID, collection.EnvID, collection.ID, collection.Schema)
-		if err != nil {
-			return false, err
-		}
-	}
-
-	return setSchema, nil
-}
-
 func (s *Setup) CheckCollections(ctx context.Context) error {
 	if len(s.Collections) == 0 {
 		return nil
@@ -207,8 +209,9 @@ func (s *Setup) CheckCollections(ctx context.Context) error {
 
 	var errs []error
 	for _, c := range s.Collections {
-		if err := s.CheckCollection(ctx, c); err != nil {
-			errs = append(errs, errors.WithDetailf(err, "Не найдена коллекция %s(%s)", c.collection.ID, c.collection.Name))
+		collection := c.Value(s)
+		if err := s.CheckCollection(ctx, collection); err != nil {
+			errs = append(errs, errors.WithDetailf(err, "Не найдена коллекция %s(%s)", collection.ID, collection.Name))
 		}
 	}
 
@@ -219,8 +222,8 @@ func (s *Setup) CheckCollections(ctx context.Context) error {
 	return nil
 }
 
-func (s *Setup) CheckCollection(ctx context.Context, c CollectionConfig) (err error) {
-	_, err = s.content.Collections.Get(ctx, s.SpaceID, s.EnvironmentID, c.collection.ID)
+func (s *Setup) CheckCollection(ctx context.Context, c *collections.Collection) (err error) {
+	_, err = s.content.Collections.Get(ctx, s.SpaceID, s.EnvironmentID, c.ID)
 	return err
 }
 
@@ -233,28 +236,30 @@ func (s *Setup) UninstallCollections(ctx context.Context) error {
 
 	for _, c := range s.Collections {
 		if err := s.UninstallCollection(ctx, c); err != nil {
+			collection := c.Value(s)
 			s.logger.Error("Failed to uninstall collection",
-				zap.String("Collection ID", c.collection.ID),
-				zap.String("Collection Name", c.collection.Name),
+				zap.String("Collection ID", collection.ID),
+				zap.String("Collection Name", collection.Name),
 				zap.Error(err),
 			)
-			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall collection"), "Возникла ошибка при удалении коллекции %s(%s)", c.collection.Name, c.collection.ID)
+			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall collection"),
+				"Возникла ошибка при удалении коллекции %s(%s)", collection.Name, collection.ID)
 		}
 	}
 
 	return nil
 }
 
-func (s *Setup) UninstallCollection(ctx context.Context, c CollectionConfig) error {
-	ok, err := c.DeleteFn(s, c.collection)
-	if err != nil {
-		return err
-	}
-	if ok {
-		if err = s.content.Collections.Delete(ctx, s.SpaceID, s.EnvironmentID, c.collection.ID); err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
+func (s *Setup) UninstallCollection(ctx context.Context, c *Collection) error {
+	collection := c.Value(s)
+	deleteCollection := c.DeleteFunc(s, collection)
+	if deleteCollection {
+		if err := s.content.Collections.Delete(ctx, s.SpaceID, s.EnvironmentID, collection.ID); err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
 			return err
 		}
-		s.removeItems(c.collection.ID) // после удаления коллекции нет смысла удалять ее элементы
+
+		// TODO: Проверить, в чем смысл происходящего
+		//s.removeItems(c.collection.ID) // после удаления коллекции нет смысла удалять ее элементы
 	}
 	return nil
 }
diff --git a/pkg/setup/collection_test.go b/pkg/setup/collection_test.go
index 43b450119a0892865486517df1d43a7e92d3dc2d..f1d9e4da1182938249192f8ef410ceecc81c743a 100644
--- a/pkg/setup/collection_test.go
+++ b/pkg/setup/collection_test.go
@@ -4,20 +4,69 @@ import (
 	"context"
 	"testing"
 
-	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	mockscollections "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/environments"
 	envmocks "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"github.com/stretchr/testify/mock"
+
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/mock"
-	"github.com/stretchr/testify/require"
 )
 
-func TestSetup_InstallCollections(t *testing.T) {
+// Некорректный тест, OverwriteCollection всегда перезаписывает информацию
+func TestCollection_UpdateExistingCollection(t *testing.T) {
+	tests := []struct {
+		name          string
+		old, new      *collections.Collection
+		wantUpdate    bool
+		wantSetSchema bool
+		wantErr       bool
+	}{
+		{
+			name:          "Equal collections",
+			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Schema: schema.New("a", field.String())},
+			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Schema: schema.New("a", field.String())},
+			wantUpdate:    false,
+			wantSetSchema: false,
+		},
+		{
+			name:          "Schema changed",
+			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll", Schema: schema.New("a", field.String())},
+			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll", Schema: schema.New("b", field.String())},
+			wantUpdate:    true,
+			wantSetSchema: true,
+		},
+		{
+			name:          "Collection name changed",
+			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll1", Schema: schema.New("a", field.String())},
+			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll2", Schema: schema.New("a", field.String())},
+			wantUpdate:    true,
+			wantSetSchema: false,
+		},
+		{
+			name:          "Collection View changed",
+			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp1", EnvID: "env1", CollectionID: "coll1"}},
+			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: "env2", CollectionID: "coll2"}},
+			wantUpdate:    true,
+			wantSetSchema: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := new(Collection)
+			UpdateExistingCollection()(c)
+			collection, update := c.UpdateFunc(new(Setup), tt.old, tt.new)
+			assert.Equal(t, tt.wantUpdate, update)
+			assert.Equal(t, tt.wantSetSchema, IsSchemaUpdateRequired(tt.old, collection))
+		})
+	}
+}
+
+func TestCollections_InstallCollections(t *testing.T) {
 	tests := []struct {
 		name            string
 		collections     []*collections.Collection
@@ -35,12 +84,19 @@ func TestSetup_InstallCollections(t *testing.T) {
 			},
 		},
 		{
-			name:        "Install one collection success",
-			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}},
+			name: "Install one collection success",
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env",
+				Schema: schema.New("name", field.String()).ClearState()}},
 			collectionsCall: func(svc *mockscollections.Collections) {
 				svc.On("Get", mock.Anything, "sp", "env", "1").Return(nil, errors.New("not found")).Once()
-				svc.On("Create", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}).Return(&collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}, nil).Once()
-				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String())).Return(nil).Once()
+				svc.On("Create", mock.Anything,
+					&collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env",
+						Schema: schema.New("name", field.String()).ClearState()}).
+					Return(&collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env",
+						Schema: schema.New("name", field.String()).ClearState()}, nil).Once()
+				svc.On("SetSchema", mock.Anything, "sp", "env", "1",
+					schema.New("name", field.String()).ClearState()).
+					Return(nil).Once()
 			},
 			envsCall: func(svc *envmocks.Environments) {
 				svc.On("Migrate", mock.Anything, "sp", "env").Return(nil).Once()
@@ -68,7 +124,9 @@ func TestSetup_InstallCollections(t *testing.T) {
 			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}},
 			collectionsCall: func(svc *mockscollections.Collections) {
 				svc.On("Get", mock.Anything, "sp", "env", "1").Return(nil, errors.New("not found")).Once()
-				svc.On("Create", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}).Return(nil, errors.New("some error")).Once()
+				svc.On("Create", mock.Anything,
+					&collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}).
+					Return(nil, errors.New("some error")).Once()
 			},
 			wantErr: func(t *testing.T, err error) {
 				assert.Error(t, err)
@@ -123,11 +181,15 @@ func TestSetup_InstallCollections(t *testing.T) {
 		},
 		{
 			name:        "Fail to install collection on migrate",
-			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}},
+			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String()).ClearState()}},
 			collectionsCall: func(svc *mockscollections.Collections) {
 				svc.On("Get", mock.Anything, "sp", "env", "1").Return(nil, errors.New("not found")).Once()
-				svc.On("Create", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}).Return(&collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}, nil).Once()
-				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String())).Return(nil).Once()
+				svc.On("Create", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env",
+					Schema: schema.New("name", field.String()).ClearState()}).
+					Return(&collections.Collection{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env",
+						Schema: schema.New("name", field.String()).ClearState()}, nil).Once()
+				svc.On("SetSchema", mock.Anything, "sp", "env", "1",
+					schema.New("name", field.String()).ClearState()).Return(nil).Once()
 			},
 			envsCall: func(svc *envmocks.Environments) {
 				svc.On("Migrate", mock.Anything, "sp", "env").Return(errors.New("migrate error")).Once()
@@ -157,52 +219,3 @@ func TestSetup_InstallCollections(t *testing.T) {
 		})
 	}
 }
-
-func TestOverwriteCollection(t *testing.T) {
-	tests := []struct {
-		name          string
-		old, new      *collections.Collection
-		wantUpdate    bool
-		wantSetSchema bool
-		wantErr       bool
-	}{
-		{
-			name:          "Equal collections should not be updated",
-			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Schema: schema.New("a", field.String())},
-			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Schema: schema.New("a", field.String())},
-			wantUpdate:    false,
-			wantSetSchema: false,
-		},
-		{
-			name:          "For collections with different schemas and equal other params schemas should be set",
-			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll", Schema: schema.New("a", field.String())},
-			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll", Schema: schema.New("b", field.String())},
-			wantUpdate:    false,
-			wantSetSchema: true,
-		},
-		{
-			name:          "Collections with different names should be updated",
-			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll1", Schema: schema.New("a", field.String())},
-			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", Name: "Coll2", Schema: schema.New("a", field.String())},
-			wantUpdate:    true,
-			wantSetSchema: false,
-		},
-		{
-			name:          "Collections with different view params should be updated",
-			old:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp1", EnvID: "env1", CollectionID: "coll1"}},
-			new:           &collections.Collection{ID: "coll", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: "env2", CollectionID: "coll2"}},
-			wantUpdate:    true,
-			wantSetSchema: false,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			c := new(CollectionConfig)
-			OverwriteCollection()(c)
-			_, update, setSchema, err := c.UpdateFn(new(Setup), tt.old, tt.new)
-			require.NoError(t, err)
-			assert.Equal(t, tt.wantUpdate, update)
-			assert.Equal(t, tt.wantSetSchema, setSchema)
-		})
-	}
-}
diff --git a/pkg/setup/config.go b/pkg/setup/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..d7f68a90409ab338f8ff191e2f91bf80ddc0b130
--- /dev/null
+++ b/pkg/setup/config.go
@@ -0,0 +1,282 @@
+package setup
+
+import (
+	"io/fs"
+
+	"github.com/pkg/errors"
+)
+
+type Config struct {
+	Collections Collections
+	Items       Items
+	Roles       Roles
+	Clients     Clients
+}
+
+func NewConfig() *Config {
+	return &Config{}
+}
+
+// Clone возвращает копию конфигурации
+func (cfg *Config) Clone() *Config {
+	return &Config{
+		Collections: cfg.Collections.Clone(),
+		Items:       cfg.Items.Clone(),
+		Roles:       cfg.Roles.Clone(),
+		Clients:     cfg.Clients.Clone(),
+	}
+}
+
+// Load загружает Config из файловой системы
+// Файлы должны быть расположены в директории со следующей структурой:
+//   - collections/ - директория с файлами конфигурации коллекций
+//   - clients/ - директория с файлами конфигурации клиентов
+//   - items/ - директория с файлами конфигурации элементов
+//   - roles/ - директория с файлами конфигурации ролей
+func (cfg *Config) Load(fsys fs.FS) (*Config, error) {
+	if _, err := fs.Stat(fsys, "."); err != nil {
+		return nil, errors.Wrapf(err, "Can't load config. (fs=%v)", fsys)
+	}
+
+	if subFS, err := fs.Sub(fsys, "collections"); err == nil {
+		if err = cfg.Collections.Load(subFS); err != nil && !errors.Is(err, fs.ErrNotExist) {
+			return nil, err
+		}
+	}
+
+	if subFS, err := fs.Sub(fsys, "items"); err == nil {
+		if err = cfg.Items.Load(subFS, DecodeItem()); err != nil && !errors.Is(err, fs.ErrNotExist) {
+			return nil, err
+		}
+	}
+
+	if subFS, err := fs.Sub(fsys, "roles"); err == nil {
+		if err = cfg.Roles.Load(subFS); err != nil && !errors.Is(err, fs.ErrNotExist) {
+			return nil, err
+		}
+	}
+
+	if subFS, err := fs.Sub(fsys, "clients"); err == nil {
+		if err = cfg.Clients.Load(subFS); err != nil && !errors.Is(err, fs.ErrNotExist) {
+			return nil, err
+		}
+	}
+
+	return cfg, nil
+}
+
+func (cfg *Config) MustLoad(fsys fs.FS) *Config {
+	c, err := cfg.Load(fsys)
+	if err != nil {
+		panic(err)
+	}
+	return c
+}
+
+// LoadItems загружает элементы из указанной файловой системы
+//func (cfg *Config) LoadClients(fsys fs.FS, opt ...ClientsOption) (*Config, error) {
+//	assets := perxis.NewAssets[*clients.Client]()
+//	cls, err := assets.FromFS(fsys)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return cfg.AddClients(cls, opt...), nil
+//}
+//
+//func (cfg *Config) MustLoadClients(fsys fs.FS, opt ...ClientsOption) *Config {
+//	c, err := cfg.LoadClients(fsys, opt...)
+//	if err != nil {
+//		panic(err)
+//	}
+//	return c
+//}
+//
+//// AddClients добавляет требования к настройке приложений в пространстве
+//func (cfg *Config) AddClients(clients []*clients.Client, opt ...ClientsOption) *Config {
+//	for _, client := range clients {
+//		cfg.AddClient(client, opt...)
+//	}
+//	return cfg
+//}
+//
+//// AddClient добавляет требования к настройке приложений в пространстве
+//func (c *Config) AddClient(client *clients.Client, opt ...ClientsOption) *Config {
+//	c.Clients = append(c.Clients, NewClientConfig(client, opt...))
+//	return c
+//}
+//
+//// LoadItems загружает элементы из указанной файловой системы
+//func (cfg *Config) LoadRoles(fsys fs.FS, opt ...RolesOption) (*Config, error) {
+//	assets := perxis.NewAssets[*roles.Role]()
+//	rls, err := assets.FromFS(fsys)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return cfg.AddRoles(rls, opt...), nil
+//}
+//
+//func (cfg *Config) MustLoadRoles(fsys fs.FS, opt ...RolesOption) *Config {
+//	c, err := cfg.LoadRoles(fsys, opt...)
+//	if err != nil {
+//		panic(err)
+//	}
+//	return c
+//}
+//
+//// AddRoles добавляет требования к настройке ролей в пространстве
+//func (cfg *Config) AddRoles(roles []*roles.Role, opt ...RolesOption) *Config {
+//	for _, role := range roles {
+//		cfg.AddRole(role, opt...)
+//	}
+//	return cfg
+//}
+//
+//// AddRole добавляет требования к настройке ролей в пространстве
+//func (cfg *Config) AddRole(role *roles.Role, opt ...RolesOption) *Config {
+//	cfg.Roles = append(cfg.Roles, NewRoleConfig(role, opt...))
+//	return cfg
+//}
+//
+//// AddCollections добавляет требования к настройке коллекций в пространстве
+//func (cfg *Config) AddCollections(collections []*collections.Collection, opt ...CollectionsOption) (*Config, error) {
+//	var errs *multierror.Error
+//	for _, col := range collections {
+//		if _, err := cfg.AddCollection(col, opt...); err != nil {
+//			errs = multierror.Append(errs, err)
+//		}
+//	}
+//	return cfg, errs.ErrorOrNil()
+//}
+//
+//// AddCollection добавляет требование к настройке коллекции в пространстве
+//func (cfg *Config) AddCollection(collection *collections.Collection, opt ...EntityOption[*collections.Collection]) (*Config, error) {
+//	collection = collection.Clone()
+//
+//	if collection.Schema != nil { // ??? Почему это здесь? Возможно, это должно быть снаружи в том месте, где создается коллекция и схема?
+//		// приведение внутренних типов схемы, чтобы избежать возможного несоответствия типов при
+//		// сравнивании схем (`[]interface{}/[]string`, `int/int64`, etc.)
+//		if err := collection.Schema.ConvertTypes(); err != nil {
+//			return nil, err
+//		}
+//	}
+//
+//	e := NewEntity(collection, opt...)
+//	DefaultUpdateCollectionStrategy()(е)
+//	DeleteCollectionIfRemove()(е)
+//
+//	cfg.Collections = append(cfg.Collections, e)
+//	return cfg, nil
+//}
+//
+//// MustAddCollection добавляет требование к настройке коллекции в пространстве
+//func (cfg *Config) MustAddCollection(collection *collections.Collection, opt ...CollectionsOption) *Config {
+//	config, err := NewCollectionConfig(collection, opt...)
+//	if err != nil {
+//		panic(err)
+//	}
+//	cfg.Collections = append(cfg.Collections, config)
+//	return cfg
+//}
+//
+//// LoadCollections загружает коллекции из указанной файловой системы
+//func (cfg *Config) LoadCollections(fsys fs.FS, opt ...CollectionsOption) (*Config, error) {
+//	colls, err := collections.FromFS(fsys)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return cfg.AddCollections(colls, opt...)
+//}
+//
+//func (cfg *Config) MustLoadCollections(fsys fs.FS, opt ...CollectionsOption) *Config {
+//	c, err := cfg.LoadCollections(fsys, opt...)
+//	if err != nil {
+//		panic(err)
+//	}
+//	return c
+//}
+//
+//// GetCollection возвращает коллекцию по идентификатору
+//func (cfg *Config) GetCollection(id string) *collections.Collection {
+//	for _, c := range cfg.Collections {
+//		if c.collection.ID == id {
+//			return c.collection
+//		}
+//	}
+//	return nil
+//}
+//
+//func (cfg *Config) GetAllCollections() []*collections.Collection {
+//	res := make([]*collections.Collection, 0, len(cfg.Collections))
+//	for _, c := range cfg.Collections {
+//		res = append(res, c.collection)
+//	}
+//	return res
+//}
+//
+//// GetCollectionConfig возвращает конфигурацию коллекции по идентификатору
+//func (cfg *Config) GetCollectionConfig(id string) *CollectionConfig {
+//	for _, c := range cfg.Collections {
+//		if c.collection.ID == id {
+//			return &c
+//		}
+//	}
+//	return nil
+//}
+//
+//// GetCollectionConfigList возвращает копию список конфигураций коллекций
+//func (cfg *Config) GetCollectionConfigList() CollectionConfigList {
+//	return cfg.Collections.Clone()
+//}
+//
+//// LoadItems загружает элементы из указанной файловой системы
+//func (cfg *Config) LoadItems(fsys fs.FS, opt ...ItemsOption) (*Config, error) {
+//	assets := perxis.NewAssets[*items.Item]()
+//	itms, err := assets.FromFS(fsys)
+//	if err != nil {
+//		return nil, err
+//	}
+//	return cfg.AddItems(itms, append(opt, DecodeItem())...), nil
+//}
+//
+//func (cfg *Config) MustLoadItems(fsys fs.FS, opt ...ItemsOption) *Config {
+//	cfg, err := cfg.LoadItems(fsys, opt...)
+//	if err != nil {
+//		panic(err)
+//	}
+//	return cfg
+//}
+//
+//// AddItems добавляет требования к настройке элементов в пространстве
+//func (cfg *Config) AddItems(items []*items.Item, opt ...ItemsOption) *Config {
+//	for _, item := range items {
+//		cfg.AddItem(item, opt...)
+//	}
+//	return cfg
+//}
+//
+//// AddItem добавляет требования к настройке элементов в пространстве
+//func (cfg *Config) AddItem(item *items.Item, opt ...ItemsOption) *Config {
+//	cfg.Items = append(cfg.Items, NewItemConfig(item, opt...))
+//	return cfg
+//}
+//
+//// GetItems возвращает элементы для указанной коллекции
+//func (cfg *Config) GetItems(collectionId string) []*items.Item {
+//	var items []*items.Item
+//	for _, i := range cfg.Items {
+//		if i.item.CollectionID == collectionId {
+//			items = append(items, i.item)
+//		}
+//	}
+//	return items
+//}
+//
+//// GetItem возвращает элемент для указанной коллекции и идентификатора
+//func (cfg *Config) GetItem(collectionId, itemId string) *items.Item {
+//	for _, i := range cfg.Items {
+//		if i.item.CollectionID == collectionId && i.item.ID == itemId {
+//			return i.item
+//		}
+//	}
+//	return nil
+//}
diff --git a/pkg/setup/config_test.go b/pkg/setup/config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5e7f114c4f04350e2389e8ee9b72db37adae5a63
--- /dev/null
+++ b/pkg/setup/config_test.go
@@ -0,0 +1,20 @@
+package setup
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// TODO
+
+func TestConfig_Load(t *testing.T) {
+	cfg := NewConfig()
+	cfg, err := cfg.Load(os.DirFS("../../assets/tests/setup"))
+	assert.NoError(t, err)
+	assert.Len(t, cfg.Collections, 3)
+	assert.Equal(t, cfg.Collections[0].Value(nil).ID, "collection_a")
+	assert.Equal(t, cfg.Collections[1].Value(nil).ID, "collection_b")
+	assert.Equal(t, cfg.Collections[2].Value(nil).ID, "collection_c")
+}
diff --git a/pkg/setup/entity.go b/pkg/setup/entity.go
new file mode 100644
index 0000000000000000000000000000000000000000..69cd2c8d5ef4224bbc270c0c8cbb8df1fdfa1bf1
--- /dev/null
+++ b/pkg/setup/entity.go
@@ -0,0 +1,179 @@
+package setup
+
+import (
+	"io/fs"
+
+	"git.perx.ru/perxis/perxis-go"
+)
+
+type Clonable[T any] interface {
+	GetID() string
+	Clone() T
+}
+
+type Conf[C any, T Clonable[T]] interface {
+	Init(e *Entity[C, T])
+}
+
+type ValueFunc[T Clonable[T]] func(s *Setup, e T)
+type FilterFunc[T Clonable[T]] func(t T) bool
+type UpdateFunc[T Clonable[T]] func(s *Setup, exist, new T) (T, bool)
+type DeleteFunc[T Clonable[T]] func(s *Setup, e T) bool
+type EntityOption[C any, T Clonable[T]] func(c *Entity[C, T])
+
+type Entity[C any, T Clonable[T]] struct {
+	value      T
+	ValueFunc  []ValueFunc[T]
+	UpdateFunc UpdateFunc[T]
+	DeleteFunc DeleteFunc[T]
+	Conf       C
+	Err        error
+}
+
+func NewEntity[C any, T Clonable[T]](val T, opt ...EntityOption[C, T]) *Entity[C, T] {
+	e := &Entity[C, T]{value: val}
+
+	var conf any = e.Conf
+	if vv, ok := conf.(Conf[C, T]); ok {
+		vv.Init(e)
+	}
+
+	for _, o := range opt {
+		o(e)
+	}
+	return e
+}
+
+// Clone возвращает копию сущности
+func (e *Entity[C, T]) Clone() *Entity[C, T] {
+	return &Entity[C, T]{
+		value:      e.value.Clone(),
+		ValueFunc:  e.ValueFunc,
+		UpdateFunc: e.UpdateFunc,
+		DeleteFunc: e.DeleteFunc,
+		Conf:       e.Conf,
+		Err:        e.Err,
+	}
+}
+
+// Value возвращает значение сущности
+func (e *Entity[C, T]) Value(s *Setup) T {
+	ent := e.value.Clone() // TODO: Мы уже используем копию всей конфигурации, нужно ли это?
+	for _, fn := range e.ValueFunc {
+		fn(s, ent)
+	}
+	return ent
+}
+
+type EntityList[C any, T Clonable[T]] []*Entity[C, T]
+
+// Clone возвращает копию списка сущностей
+func (l *EntityList[C, T]) Clone() EntityList[C, T] {
+	var res EntityList[C, T]
+	for _, e := range *l {
+		res = append(res, e.Clone())
+	}
+	return res
+}
+
+// Add добавляет сущности в список EntityList
+func (l *EntityList[C, T]) Add(t T, opt ...EntityOption[C, T]) {
+	e := NewEntity(t.Clone(), opt...)
+	*l = append(*l, e)
+}
+
+// AddMany добавляет несколько сущностей в список EntityList
+func (l *EntityList[C, T]) AddMany(t []T, opt ...EntityOption[C, T]) {
+	for _, item := range t {
+		l.Add(item, opt...)
+	}
+}
+
+// Filter возвращает список сущностей, удовлетворяющих фильтру
+func (l *EntityList[C, T]) Filter(filter FilterFunc[T]) *EntityList[C, T] {
+	res := make(EntityList[C, T], 0)
+	for _, e := range *l {
+		if filter(e.value) {
+			res = append(res, e)
+		}
+	}
+	return &res
+}
+
+// Get возвращает конфигурацию по ID
+func (l *EntityList[C, T]) Get(id string) *Entity[C, T] {
+	for _, e := range *l {
+		if e.value.GetID() == id {
+			return e
+		}
+	}
+	return nil
+}
+
+// GetIDs возвращает список ID сущностей
+func (l *EntityList[C, T]) GetIDs() []string {
+	var res []string
+	for _, e := range *l {
+		res = append(res, e.value.GetID())
+	}
+	return res
+}
+
+// Load загружает сущности в список EntityList из указанной файловой системы
+func (l *EntityList[C, T]) Load(fsys fs.FS, opt ...EntityOption[C, T]) error {
+	assets := perxis.NewAssets[T]()
+	items, err := assets.FromFS(fsys)
+	if err != nil {
+		return err
+	}
+	for _, item := range items {
+		l.Add(item, opt...)
+	}
+	return nil
+}
+
+// MustLoad загружает сущности в список EntityList из указанной файловой системы
+func (l *EntityList[C, T]) MustLoad(fsys fs.FS, opt ...EntityOption[C, T]) {
+	if err := l.Load(fsys, opt...); err != nil {
+		panic(err)
+	}
+}
+
+// WithOptions добавляет опции к сущности в списке EntityList согласно фильтру
+func (l *EntityList[C, T]) WithOptions(filter FilterFunc[T], opt ...EntityOption[C, T]) {
+	for _, e := range *l {
+		if filter(e.value) {
+			for _, o := range opt {
+				o(e)
+			}
+		}
+	}
+}
+
+// Overwrite перезаписывает сущность при обновлении
+func Overwrite[C any, T Clonable[T]]() EntityOption[C, T] {
+	return func(c *Entity[C, T]) {
+		c.UpdateFunc = func(s *Setup, old, new T) (T, bool) { return new, true }
+	}
+}
+
+// Keep сохраняет сущность при обновлении
+func Keep[C any, T Clonable[T]]() EntityOption[C, T] {
+	return func(c *Entity[C, T]) {
+		c.UpdateFunc = func(s *Setup, old, new T) (T, bool) { return old, false }
+	}
+}
+
+// Delete удаляет сущность
+func Delete[C any, T Clonable[T]]() EntityOption[C, T] {
+	return func(c *Entity[C, T]) {
+		c.DeleteFunc = func(s *Setup, e T) bool { return true }
+	}
+}
+
+// DeleteIfRemoveFlag удаляет сущность если флаг Setup Remove установлен
+func DeleteIfRemoveFlag[C any, T Clonable[T]]() EntityOption[C, T] {
+	return func(c *Entity[C, T]) {
+		c.DeleteFunc = func(s *Setup, e T) bool { return s.IsRemove() }
+	}
+}
diff --git a/pkg/setup/entity_test.go b/pkg/setup/entity_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fd02d186a89504001954b4eeb041a3a21a1c64ce
--- /dev/null
+++ b/pkg/setup/entity_test.go
@@ -0,0 +1,47 @@
+package setup
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestEntityList_WithOptions(t *testing.T) {
+	colls := make(Collections, 0)
+	colls.Add(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env"})
+	colls.Add(&collections.Collection{ID: "2", SpaceID: "sp", EnvID: "env"})
+	colls.Add(&collections.Collection{ID: "3", SpaceID: "sp", EnvID: "env"})
+	colls.Add(&collections.Collection{ID: "4", SpaceID: "sp", EnvID: "env"})
+	colls.WithOptions(func(c *collections.Collection) bool { return c.ID == "1" || c.ID == "3" }, func(c *Collection) {
+		c.UpdateFunc = nil
+	})
+
+	assert.Nil(t, colls[0].UpdateFunc)
+	assert.NotNil(t, colls[1].UpdateFunc)
+	assert.Nil(t, colls[2].UpdateFunc)
+	assert.NotNil(t, colls[3].UpdateFunc)
+}
+
+func TestEntityList_Add(t *testing.T) {
+	c := &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env"}
+	colls := make(Collections, 0)
+	colls.Add(c)
+	colls.Add(c)
+	assert.Equal(t, colls[0].value, c)
+	assert.Equal(t, colls[1].value, c)
+	assert.True(t, colls[0].value != c)
+	assert.True(t, colls[1].value != c)
+	assert.True(t, colls[0].value != colls[1].value)
+}
+
+func TestEntityList_Get(t *testing.T) {
+	c := &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env"}
+	colls := make(Collections, 0)
+	colls.Add(c)
+	c1 := colls[0].Value(nil)
+	assert.Equal(t, c, c1)
+	assert.Equal(t, colls[0].value, c1)
+	assert.True(t, c != c1)
+	assert.True(t, colls[0].value != c1)
+}
diff --git a/pkg/setup/item.go b/pkg/setup/item.go
index a17512d0c0ce38714cc52cd7752ec7149ae292b3..adf79935a7e55c212687907f0118112adfcc97ff 100644
--- a/pkg/setup/item.go
+++ b/pkg/setup/item.go
@@ -5,9 +5,11 @@ import (
 	"reflect"
 	"strings"
 
+	"go.uber.org/zap"
+
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/items"
-	"go.uber.org/zap"
 )
 
 var (
@@ -17,44 +19,60 @@ var (
 	ErrItemsNotFound  = errors.New("item not found")
 )
 
-type ItemsOption func(c *ItemConfig)
-type PublishItemFn func(s *Setup, item *items.Item) (*items.Item, bool)
-type UpdateItemFn func(s *Setup, exist, new *items.Item) (*items.Item, bool)
-type DeleteItemFn func(s *Setup, col *items.Item) bool
+type (
+	Item       = Entity[ItemConf, *items.Item]
+	Items      = EntityList[ItemConf, *items.Item]
+	ItemOption = EntityOption[ItemConf, *items.Item]
 
-type ItemConfig struct {
-	item      *items.Item
-	PublishFn PublishItemFn
-	UpdateFn  UpdateItemFn
-	DeleteFn  DeleteItemFn
-}
+	ItemConf struct {
+		PublishFunc func(s *Setup, item *items.Item) (*items.Item, bool)
+		encoded     bool // Если запись загружена из файла, необходимо выполнить Decode перед установкой
+	}
+)
 
-func NewItemConfig(item *items.Item, opt ...ItemsOption) ItemConfig {
-	c := ItemConfig{item: item}
+var (
+	NewItem                = NewEntity[ItemConf, *items.Item]
+	OverwriteItem          = Overwrite[ItemConf, *items.Item]
+	KeepExistingItem       = Keep[ItemConf, *items.Item]
+	DeleteItem             = Delete[ItemConf, *items.Item]
+	DeleteItemIfRemoveFlag = DeleteIfRemoveFlag[ItemConf, *items.Item]
+)
 
-	PublishItem()(&c)
-	KeepExistingItem()(&c)
-	DeleteItemIfRemove()(&c)
+func (ItemConf) Init(e *Entity[ItemConf, *items.Item]) {
+	PublishItem()(e)
+	KeepExistingItem()(e)
+	DeleteItemIfRemoveFlag()(e)
+}
 
-	for _, o := range opt {
-		o(&c)
+// OverwriteItem перезаписывает элемент
+func PublishItem() ItemOption {
+	return func(c *Item) {
+		c.Conf.PublishFunc = func(s *Setup, item *items.Item) (*items.Item, bool) { return item, true }
 	}
-
-	return c
 }
 
-func OverwriteItem() ItemsOption {
-	return func(c *ItemConfig) {
-		c.UpdateFn = func(s *Setup, old, new *items.Item) (*items.Item, bool) { return new, true }
+// DraftItem не публикует элемент, сохраняет его в черновике
+func DraftItem() ItemOption {
+	return func(c *Item) {
+		c.Conf.PublishFunc = func(s *Setup, item *items.Item) (*items.Item, bool) { return item, false }
 	}
 }
 
-func OverwriteFields(fields ...string) ItemsOption {
-	return func(c *ItemConfig) {
-		c.UpdateFn = func(s *Setup, old, new *items.Item) (*items.Item, bool) {
+// DecodeItem декодирует элемент перед установкой
+func DecodeItem() ItemOption {
+	return func(c *Item) {
+		c.Conf.encoded = true
+	}
+}
 
+// OverwriteFields разрешает перезаписывать указанные поля элемента, в противном случае считается что элемент не изменился
+func OverwriteFields(fields ...string) ItemOption {
+	return func(c *Item) {
+		c.UpdateFunc = func(s *Setup, old, new *items.Item) (*items.Item, bool) {
 			var changed bool
+
 			for _, field := range fields {
+				// Пропускаем системные поля
 				if items.IsSystemField(field) {
 					continue
 				}
@@ -63,8 +81,8 @@ func OverwriteFields(fields ...string) ItemsOption {
 				if err != nil {
 					continue
 				}
-
 				oldValue, err := old.Get(field)
+
 				if err != nil || newValue != oldValue {
 					changed = true
 					if err = old.Set(field, newValue); err != nil {
@@ -78,9 +96,10 @@ func OverwriteFields(fields ...string) ItemsOption {
 	}
 }
 
-func KeepFields(fields ...string) ItemsOption {
-	return func(c *ItemConfig) {
-		c.UpdateFn = func(s *Setup, old, new *items.Item) (*items.Item, bool) {
+// KeepFields сохраняет указанные поля элемента
+func KeepFields(fields ...string) ItemOption {
+	return func(c *Item) {
+		c.UpdateFunc = func(s *Setup, old, new *items.Item) (*items.Item, bool) {
 
 			for _, field := range fields {
 				if items.IsSystemField(field) {
@@ -105,56 +124,104 @@ func KeepFields(fields ...string) ItemsOption {
 	}
 }
 
-func KeepExistingItem() ItemsOption {
-	return func(c *ItemConfig) {
-		c.UpdateFn = func(s *Setup, old, new *items.Item) (*items.Item, bool) { return old, false }
-	}
-}
+//
+// Item Setup
+//
 
-func DeleteItem() ItemsOption {
-	return func(c *ItemConfig) {
-		c.DeleteFn = func(s *Setup, item *items.Item) bool { return true }
-	}
+// AddItem добавляет требования к настройке элементов в пространстве
+func (s *Setup) AddItem(item *items.Item, opt ...ItemOption) *Setup {
+	s.Config.Items.Add(item, opt...)
+	return s
 }
 
-func DeleteItemIfRemove() ItemsOption {
-	return func(c *ItemConfig) {
-		c.DeleteFn = func(s *Setup, item *items.Item) bool { return s.IsRemove() }
-	}
+// AddItems добавляет требования к настройке элементов в пространстве
+func (s *Setup) AddItems(items []*items.Item, opt ...ItemOption) *Setup {
+	s.Config.Items.AddMany(items, opt...)
+	return s
 }
 
-func PublishItem() ItemsOption {
-	return func(c *ItemConfig) {
-		c.PublishFn = func(s *Setup, item *items.Item) (*items.Item, bool) { return item, true }
+// InstallItem настраивает элемент
+func (s *Setup) InstallItem(ctx context.Context, exists map[string]*items.Item, c *Item) error {
+	item := c.Value(s)
+	item.SpaceID, item.EnvID = s.SpaceID, s.EnvironmentID
+
+	exist, itemExists := exists[item.ID]
+	// Если элемент не существует, создаем его
+	if !itemExists {
+		if item, publish := c.Conf.PublishFunc(s, item); publish {
+			return items.CreateAndPublishItem(ctx, s.content.Items, item)
+		}
+		if _, err := s.content.Items.Create(ctx, item); err != nil {
+			return errors.Wrap(err, "create item")
+		}
+		return nil
 	}
-}
 
-func KeepDraft() ItemsOption {
-	return func(c *ItemConfig) {
-		c.PublishFn = func(s *Setup, item *items.Item) (*items.Item, bool) { return item, false }
+	// Если элемент существует, обновляем его
+	if item, changed := c.UpdateFunc(s, exist, item); changed {
+		if _, publish := c.Conf.PublishFunc(s, item); publish {
+			return items.UpdateAndPublishItem(ctx, s.content.Items, item)
+		}
+		if err := s.content.Items.Update(ctx, item); err != nil {
+			return errors.Wrap(err, "update item")
+		}
+		if err := s.content.Items.Unpublish(ctx, item); err != nil {
+			return errors.Wrap(err, "unpublish item")
+		}
+		return nil
 	}
+
+	return nil
 }
 
-func (s *Setup) InstallItems(ctx context.Context) error {
+// InstallItems устанавливает все элементы
+func (s *Setup) InstallItems(ctx context.Context) (err error) {
 	if len(s.Items) == 0 {
 		return nil
 	}
 
-	s.logger.Debug("Install items", zap.Int("Items", len(s.Items)))
+	s.logger.Debug("Installing items", zap.Int("Items", len(s.Items)))
+
+	for collID, itms := range groupByCollection(s.Items) {
+		var coll *collections.Collection
+
+		for i, c := range itms {
+			// Пропускаем элементы, которые не требуют декодирования
+			if !c.Conf.encoded {
+				continue
+			}
+
+			// Получаем коллекцию и схему для декодирования элемента
+			if coll == nil {
+				coll, err = s.content.Collections.Get(ctx, s.SpaceID, s.EnvironmentID, collID)
+				if err != nil {
+					return err
+				}
+			}
+
+			// Декодируем элемент
+			decoded, err := c.value.Decode(ctx, coll.Schema)
+			if err != nil {
+				return err
+			}
+
+			itms[i].value = decoded
+		}
 
-	for col, itms := range s.groupByCollection() {
-		exists, err := s.getExisting(ctx, col, itms)
+		// Получаем существующие элементы
+		exists, err := s.getItems(ctx, collID, itms)
 		if err != nil {
 			return err
 		}
+
+		// Устанавливаем элементы
 		for _, c := range itms {
 			if err := s.InstallItem(ctx, exists, c); err != nil {
-				s.logger.Error("Failed to install item",
-					zap.String("ID", c.item.ID),
-					zap.String("Collection", c.item.CollectionID),
-					zap.Error(err),
-				)
-				return errors.WithDetailf(errors.Wrap(err, "failed to install item"), "Возникла ошибка при добавлении элемента %s(%s)", c.item.ID, c.item.CollectionID)
+				item := c.Value(s)
+				s.logger.Error("Failed to install item", zap.String("ID", item.ID),
+					zap.String("Collection", item.CollectionID), zap.Error(err))
+				return errors.WithDetailf(errors.Wrap(err, "failed to install item"),
+					"Возникла ошибка при добавлении элемента %s(%s)", item.ID, item.CollectionID)
 			}
 		}
 	}
@@ -162,35 +229,15 @@ func (s *Setup) InstallItems(ctx context.Context) error {
 	return nil
 }
 
-func (s *Setup) InstallItem(ctx context.Context, exists map[string]*items.Item, c ItemConfig) error {
-	item := c.item
-	item.SpaceID, item.EnvID = s.SpaceID, s.EnvironmentID
-
-	exist, ok := exists[item.ID]
-	if !ok {
-		if item, publish := c.PublishFn(s, item); publish {
-			return items.CreateAndPublishItem(ctx, s.content.Items, item)
-		}
-		if _, err := s.content.Items.Create(ctx, item); err != nil {
-			return errors.Wrap(err, "create item")
-		}
-		return nil
-	}
-
-	if item, changed := c.UpdateFn(s, exist, item); changed {
-		if _, publish := c.PublishFn(s, item); publish {
-			return items.UpdateAndPublishItem(ctx, s.content.Items, item)
-		}
-		if err := s.content.Items.Update(ctx, item); err != nil {
-			return errors.Wrap(err, "update item")
-		}
-		if err := s.content.Items.Unpublish(ctx, item); err != nil {
-			return errors.Wrap(err, "unpublish item")
+// UninstallItem удаляет элемент
+func (s *Setup) UninstallItem(ctx context.Context, c *Item) error {
+	item := c.Value(s)
+	if c.DeleteFunc(s, item) {
+		err := s.content.Items.Delete(ctx, &items.Item{SpaceID: s.SpaceID, EnvID: s.EnvironmentID, CollectionID: item.CollectionID, ID: item.ID})
+		if err != nil && !strings.Contains(err.Error(), items.ErrNotFound.Error()) {
+			return err
 		}
-		return nil
-
 	}
-
 	return nil
 }
 
@@ -203,28 +250,18 @@ func (s *Setup) UninstallItems(ctx context.Context) error {
 
 	for _, c := range s.Items {
 		if err := s.UninstallItem(ctx, c); err != nil {
-			s.logger.Error("Failed to uninstall item",
-				zap.String("Item", c.item.ID),
-				zap.String("Item", c.item.CollectionID),
-				zap.Error(err),
+			item := c.Value(s)
+			s.logger.Error("Failed to uninstall item", zap.String("Item", item.ID),
+				zap.String("Item", item.CollectionID), zap.Error(err),
 			)
-			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall item"), "Возникла ошибка при удалении элемента %s(%s)", c.item.ID, c.item.CollectionID)
+			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall item"),
+				"Возникла ошибка при удалении элемента %s(%s)", item.ID, item.CollectionID)
 		}
 	}
 
 	return nil
 }
 
-func (s *Setup) UninstallItem(ctx context.Context, c ItemConfig) error {
-	if c.DeleteFn(s, c.item) {
-		err := s.content.Items.Delete(ctx, &items.Item{SpaceID: s.SpaceID, EnvID: s.EnvironmentID, CollectionID: c.item.CollectionID, ID: c.item.ID})
-		if err != nil && !strings.Contains(err.Error(), items.ErrNotFound.Error()) {
-			return err
-		}
-	}
-	return nil
-}
-
 func (s *Setup) CheckItems(ctx context.Context) error {
 	if len(s.Items) == 0 {
 		return nil
@@ -233,15 +270,17 @@ func (s *Setup) CheckItems(ctx context.Context) error {
 	var errs []error
 	s.logger.Debug("Check items", zap.Int("Items", len(s.Items)))
 
-	for col, itms := range s.groupByCollection() {
-		exists, err := s.getExisting(ctx, col, itms)
+	for col, itms := range groupByCollection(s.Items) {
+		exists, err := s.getItems(ctx, col, itms)
 		if err != nil {
 			return err
 		}
 
 		for _, c := range itms {
-			if _, ok := exists[c.item.ID]; !ok {
-				errs = append(errs, errors.WithDetailf(errors.New("not found"), "Не найден элемент %s(%s)", c.item.ID, c.item.CollectionID))
+			item := c.Value(s)
+			if _, ok := exists[item.ID]; !ok {
+				errs = append(errs, errors.WithDetailf(errors.New("not found"),
+					"Не найден элемент %s(%s)", item.ID, item.CollectionID))
 			}
 		}
 	}
@@ -253,44 +292,44 @@ func (s *Setup) CheckItems(ctx context.Context) error {
 	return nil
 }
 
-func (s *Setup) removeItems(collID string) {
-	itms := make([]ItemConfig, 0, len(s.Items))
-	for _, i := range s.Items {
-		if i.item.CollectionID != collID {
-			itms = append(itms, i)
-		}
-	}
-	s.Items = itms
-}
-
-func (s *Setup) groupByCollection() map[string][]ItemConfig {
-	itemsByColl := map[string][]ItemConfig{}
-	for _, i := range s.Items {
-		cfg, ok := itemsByColl[i.item.CollectionID]
+//
+//func (s *Setup) removeItems(collID string) {
+//	itms := make([]ItemConfig, 0, len(s.Items))
+//	for _, i := range s.Items {
+//		if i.item.CollectionID != collID {
+//			itms = append(itms, i)
+//		}
+//	}
+//	s.Items = itms
+//}
+
+func groupByCollection(itms Items) map[string]Items {
+	res := map[string]Items{}
+	for _, conf := range itms {
+		collectionID := conf.value.CollectionID
+		l, ok := res[collectionID]
 		if !ok {
-			itemsByColl[i.item.CollectionID] = []ItemConfig{i}
-			continue
+			res[collectionID] = make(Items, 0, 1)
 		}
-		itemsByColl[i.item.CollectionID] = append(cfg, i)
+		res[collectionID] = append(l, conf)
 	}
-	return itemsByColl
+	return res
 }
 
-func (s *Setup) getExisting(ctx context.Context, collID string, configs []ItemConfig) (map[string]*items.Item, error) {
+func (s *Setup) getItems(ctx context.Context, collID string, confs Items) (map[string]*items.Item, error) {
 	itms, _, err := s.content.Items.Find(
 		ctx,
 		s.SpaceID,
 		s.EnvironmentID,
 		collID,
-		&items.Filter{ID: getItemIds(configs)},
+		&items.Filter{ID: confs.GetIDs()},
 		&items.FindOptions{Regular: true, Hidden: true, Templates: true},
 	)
+
 	if err != nil {
-		s.logger.Error("Failed to find existing items",
-			zap.String("Collection", collID),
-			zap.Error(err),
-		)
-		return nil, errors.WithDetailf(errors.Wrap(err, "failed to find existing items"), "Возникла ошибка при поиске элементов в коллекции %s", collID)
+		s.logger.Error("Failed to find existing items", zap.String("Collection", collID), zap.Error(err))
+		return nil, errors.WithDetailf(errors.Wrap(err, "failed to find existing items"),
+			"Возникла ошибка при получении элементов в коллекции %s", collID)
 	}
 
 	exists := make(map[string]*items.Item, len(itms))
@@ -299,11 +338,3 @@ func (s *Setup) getExisting(ctx context.Context, collID string, configs []ItemCo
 	}
 	return exists, nil
 }
-
-func getItemIds(items []ItemConfig) []string {
-	var ids []string
-	for _, i := range items {
-		ids = append(ids, i.item.ID)
-	}
-	return ids
-}
diff --git a/pkg/setup/item_test.go b/pkg/setup/item_test.go
index 2d2e1a7705a80be6d4eb783af5ad81daf431e886..0a128625e7871d20aad0bceff7561197e97c01ea 100644
--- a/pkg/setup/item_test.go
+++ b/pkg/setup/item_test.go
@@ -6,10 +6,11 @@ import (
 
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
-	"git.perx.ru/perxis/perxis-go/pkg/items"
 	itemsMock "git.perx.ru/perxis/perxis-go/pkg/items/mocks"
-	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
+
+	"git.perx.ru/perxis/perxis-go/pkg/items"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
@@ -82,17 +83,16 @@ func TestItem_OverwriteFields(t *testing.T) {
 			changed: false,
 		},
 	}
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			c := ItemConfig{item: test.new}
-			OverwriteFields(test.fields...)(&c)
-
-			got, changed := c.UpdateFn(nil, test.old, test.new)
-			require.Equal(t, test.changed, changed)
-			if !test.changed {
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := NewItem(tt.new)
+			OverwriteFields(tt.fields...)(c)
+			got, changed := c.UpdateFunc(nil, tt.old, tt.new)
+			require.Equal(t, tt.changed, changed)
+			if !tt.changed {
 				return
 			}
-			assert.Equal(t, test.want, got)
+			assert.Equal(t, tt.want, got)
 
 		})
 	}
@@ -172,24 +172,22 @@ func TestItem_KeepFields(t *testing.T) {
 			changed: true,
 		},
 	}
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			c := ItemConfig{item: test.new}
-			KeepFields(test.fields...)(&c)
-
-			got, changed := c.UpdateFn(nil, test.old, test.new)
-			require.Equal(t, test.changed, changed)
-			if !test.changed {
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := NewItem(tt.new)
+			KeepFields(tt.fields...)(c)
+			got, changed := c.UpdateFunc(nil, tt.old, tt.new)
+			require.Equal(t, tt.changed, changed)
+			if !tt.changed {
 				return
 			}
-			assert.Equal(t, test.want, got)
+			assert.Equal(t, tt.want, got)
 
 		})
 	}
 }
 
 func TestSetup_InstallItems(t *testing.T) {
-
 	tests := []struct {
 		name      string
 		items     []*items.Item
@@ -279,7 +277,6 @@ func TestSetup_InstallItems(t *testing.T) {
 }
 
 func TestSetup_CreateDraft(t *testing.T) {
-
 	tests := []struct {
 		name      string
 		items     []*items.Item
@@ -322,14 +319,13 @@ func TestSetup_CreateDraft(t *testing.T) {
 			}
 
 			s := NewSetup(&content.Content{Items: i}, "sp", "env", nil)
-			s.AddItems(tt.items, KeepDraft())
+			s.AddItems(tt.items, DraftItem())
 			tt.wantErr(t, s.InstallItems(context.Background()))
 		})
 	}
 }
 
 func TestSetup_UpdateDraft(t *testing.T) {
-
 	tests := []struct {
 		name      string
 		items     []*items.Item
@@ -358,7 +354,7 @@ func TestSetup_UpdateDraft(t *testing.T) {
 			}
 
 			s := NewSetup(&content.Content{Items: i}, "sp", "env", nil)
-			s.AddItems(tt.items, KeepDraft())
+			s.AddItems(tt.items, DraftItem())
 			tt.wantErr(t, s.InstallItems(context.Background()))
 		})
 	}
diff --git a/pkg/setup/role.go b/pkg/setup/role.go
index ac6e641a97c6160cf29c5353f0176ef110a9bab3..2498e8456c474331ce648dbf48caaccf16718bb3 100644
--- a/pkg/setup/role.go
+++ b/pkg/setup/role.go
@@ -4,11 +4,12 @@ import (
 	"context"
 	"strings"
 
+	"go.uber.org/zap"
+
 	"git.perx.ru/perxis/perxis-go/pkg/data"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/permission"
 	"git.perx.ru/perxis/perxis-go/pkg/roles"
-	"go.uber.org/zap"
 )
 
 var (
@@ -17,67 +18,28 @@ var (
 	ErrUninstallRoles = errors.New("failed to uninstall role")
 )
 
-type RolesOption func(c *RoleConfig)
-type UpdateRoleFn func(s *Setup, exist, new *roles.Role) (*roles.Role, bool)
-type DeleteRoleFn func(s *Setup, role *roles.Role) bool
-
-type RoleConfig struct {
-	role     *roles.Role
-	UpdateFn UpdateRoleFn
-	DeleteFn DeleteRoleFn
-}
-
-func NewRoleConfig(role *roles.Role, opt ...RolesOption) RoleConfig {
-	c := RoleConfig{role: role}
-
-	UpdateExistingRole()(&c)
-	DeleteRoleIfRemove()(&c)
-
-	for _, o := range opt {
-		o(&c)
-	}
-	return c
-}
-
-func OverwriteRole() RolesOption {
-	return func(c *RoleConfig) {
-		c.UpdateFn = func(s *Setup, old, new *roles.Role) (*roles.Role, bool) { return new, true }
-	}
-}
-
-//func OverwriteRoleIfChanged() RolesOption {
-//	return func(c *RoleConfig) {
-//		c.UpdateFn = func(s *Setup, old, new *roles.Role) (*roles.Role, bool) {
-//			changed := old.Description != new.Description || old.AllowManagement != new.AllowManagement ||
-//				!util.ElementsMatch(old.Environments, new.Environments)
-//			return new, changed
-//		}
-//	}
-//}
-
-func KeepExistingRole() RolesOption {
-	return func(c *RoleConfig) {
-		c.UpdateFn = func(s *Setup, old, new *roles.Role) (*roles.Role, bool) { return old, false }
-	}
-}
-
-func DeleteRole() RolesOption {
-	return func(c *RoleConfig) {
-		c.DeleteFn = func(s *Setup, role *roles.Role) bool { return true }
-	}
-}
+type Role = Entity[RoleConf, *roles.Role]
+type Roles = EntityList[RoleConf, *roles.Role]
+type RoleOption = EntityOption[RoleConf, *roles.Role]
+type RoleConf struct{}
 
-func DeleteRoleIfRemove() RolesOption {
-	return func(c *RoleConfig) {
-		c.DeleteFn = func(s *Setup, role *roles.Role) bool { return s.IsRemove() }
-	}
+// Init инициализирует конфигурацию коллекции
+func (RoleConf) Init(e *Entity[RoleConf, *roles.Role]) {
+	DefaultRoleUpdateFunc()(e)
+	DeleteRoleIfRemoveFlag()(e)
 }
 
-func UpdateExistingRole() RolesOption {
-	return func(c *RoleConfig) {
-		c.UpdateFn = func(s *Setup, exist, new *roles.Role) (*roles.Role, bool) {
+var (
+	NewRole                = NewEntity[RoleConf, *roles.Role]
+	OverwriteRole          = Overwrite[RoleConf, *roles.Role]
+	KeepExistingRole       = Keep[RoleConf, *roles.Role]
+	DeleteRole             = Delete[RoleConf, *roles.Role]
+	DeleteRoleIfRemoveFlag = DeleteIfRemoveFlag[RoleConf, *roles.Role]
+)
 
-			// если передан флаг force, то обновляем все поля роли на переданные
+func DefaultRoleUpdateFunc() RoleOption {
+	return func(c *Role) {
+		c.UpdateFunc = func(s *Setup, exist, new *roles.Role) (*roles.Role, bool) {
 			if s.IsForce() {
 				return new, true
 			}
@@ -105,68 +67,74 @@ func UpdateExistingRole() RolesOption {
 	}
 }
 
-func (s *Setup) InstallRoles(ctx context.Context) error {
-	if len(s.Roles) == 0 {
-		return nil
-	}
-
-	s.logger.Debug("Install role", zap.String("Space ID", s.SpaceID), zap.Int("Roles", len(s.Roles)))
+//
+// Role Setup
+//
 
-	for _, c := range s.Roles {
-		if err := s.InstallRole(ctx, c); err != nil {
-			s.logger.Error("Failed to install role", zap.String("Role ID", c.role.ID), zap.Error(err))
-			return errors.Wrap(errors.WithDetailf(err, "Возникла ошибка при настройке роли %s(%s)", c.role.ID, c.role.Description), "failed to install role")
-		}
-	}
+// AddRoles добавляет требования к настройке ролей в пространстве
+func (s *Setup) AddRoles(roles []*roles.Role, opt ...RoleOption) *Setup {
+	s.Config.Roles.AddMany(roles, opt...)
+	return s
+}
 
-	return nil
+// AddRole добавляет требования к настройке элементов в пространстве
+func (s *Setup) AddRole(role *roles.Role, opt ...RoleOption) *Setup {
+	s.Config.Roles.Add(role, opt...)
+	return s
 }
 
-func (s *Setup) InstallRole(ctx context.Context, c RoleConfig) error {
-	role := c.role
+// InstallRole устанавливает роль
+func (s *Setup) InstallRole(ctx context.Context, c *Role) error {
+	role := c.Value(s)
 	role.SpaceID = s.SpaceID
 
-	if !data.Contains(s.EnvironmentID, c.role.Environments) {
+	if !data.Contains(s.EnvironmentID, role.Environments) {
 		role.Environments = append(role.Environments, s.EnvironmentID)
 	}
 
 	exist, err := s.content.Roles.Get(ctx, s.SpaceID, role.ID)
+
+	// Если роль не найдена, создаем новую
 	if err != nil {
 		if !strings.Contains(err.Error(), roles.ErrNotFound.Error()) {
 			return err
 		}
-
 		_, err = s.content.Roles.Create(ctx, role)
 		return err
 	}
 
-	if r, upd := c.UpdateFn(s, exist, role); upd {
+	// Если роль найдена, обновляем ее
+	if r, needUpdate := c.UpdateFunc(s, exist, role); needUpdate {
 		return s.content.Roles.Update(ctx, r)
 	}
 
 	return nil
 }
 
-func (s *Setup) UninstallRoles(ctx context.Context) error {
+// InstallRoles устанавливает все роли
+func (s *Setup) InstallRoles(ctx context.Context) error {
 	if len(s.Roles) == 0 {
 		return nil
 	}
 
-	s.logger.Debug("Uninstall role", zap.String("Space ID", s.SpaceID), zap.Int("Roles", len(s.Roles)))
+	s.logger.Debug("Install roles", zap.String("Space ID", s.SpaceID), zap.Int("Roles", len(s.Roles)))
 
 	for _, c := range s.Roles {
-		if err := s.UninstallRole(ctx, c); err != nil {
-			s.logger.Error("Failed to uninstall role", zap.String("Role ID", c.role.ID), zap.Error(err))
-			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall role"), "Возникла ошибка при удалении роли %s(%s)", c.role.ID, c.role.Description)
+		if err := s.InstallRole(ctx, c); err != nil {
+			role := c.Value(s)
+			s.logger.Error("Failed to install role", zap.String("Role ID", role.ID), zap.Error(err))
+			return errors.Wrap(errors.WithDetailf(err, "Возникла ошибка при настройке роли %s(%s)", role.ID, role.Description), "failed to install role")
 		}
 	}
 
 	return nil
 }
 
-func (s *Setup) UninstallRole(ctx context.Context, c RoleConfig) error {
-	if c.DeleteFn(s, c.role) {
-		err := s.content.Roles.Delete(ctx, s.SpaceID, c.role.ID)
+// UninstallRole удаляет роль
+func (s *Setup) UninstallRole(ctx context.Context, c *Role) error {
+	role := c.Value(s)
+	if c.DeleteFunc(s, role) {
+		err := s.content.Roles.Delete(ctx, s.SpaceID, role.ID)
 		if err != nil && !strings.Contains(err.Error(), roles.ErrNotFound.Error()) {
 			return err
 		}
@@ -174,6 +142,34 @@ func (s *Setup) UninstallRole(ctx context.Context, c RoleConfig) error {
 	return nil
 }
 
+// UninstallRoles удаляет все роли
+func (s *Setup) UninstallRoles(ctx context.Context) error {
+	if len(s.Roles) == 0 {
+		return nil
+	}
+
+	s.logger.Debug("Uninstall role", zap.String("Space ID", s.SpaceID), zap.Int("Roles", len(s.Roles)))
+
+	for _, c := range s.Roles {
+		if err := s.UninstallRole(ctx, c); err != nil {
+			role := c.Value(s)
+			s.logger.Error("Failed to uninstall role", zap.String("Role ID", role.ID), zap.Error(err))
+			return errors.WithDetailf(errors.Wrap(err, "failed to uninstall role"),
+				"Возникла ошибка при удалении роли %s(%s)", role.ID, role.Description)
+		}
+	}
+
+	return nil
+}
+
+// CheckRole проверяет наличие роли
+func (s *Setup) CheckRole(ctx context.Context, c *Role) error {
+	role := c.Value(s)
+	_, err := s.content.Roles.Get(ctx, s.SpaceID, role.ID)
+	return err
+}
+
+// CheckRoles проверяет наличие всех ролей
 func (s *Setup) CheckRoles(ctx context.Context) error {
 	if len(s.Roles) == 0 {
 		return nil
@@ -183,8 +179,9 @@ func (s *Setup) CheckRoles(ctx context.Context) error {
 
 	var errs []error
 	for _, c := range s.Roles {
-		if err := s.CheckRole(ctx, c.role); err != nil {
-			errs = append(errs, errors.WithDetailf(err, "Не найдена роль %s(%s)", c.role.ID, c.role.Description))
+		if err := s.CheckRole(ctx, c); err != nil {
+			role := c.Value(s)
+			errs = append(errs, errors.WithDetailf(err, "Не найдена роль %s(%s)", role.ID, role.Description))
 		}
 	}
 
@@ -194,8 +191,3 @@ func (s *Setup) CheckRoles(ctx context.Context) error {
 
 	return nil
 }
-
-func (s *Setup) CheckRole(ctx context.Context, role *roles.Role) error {
-	_, err := s.content.Roles.Get(ctx, s.SpaceID, role.ID)
-	return err
-}
diff --git a/pkg/setup/setup.go b/pkg/setup/setup.go
index b087337fcb57e3cb1a76cad1e3354291b944bdb0..8855269638d1901b553ee4a6ecc6ed2c488f3d17 100644
--- a/pkg/setup/setup.go
+++ b/pkg/setup/setup.go
@@ -2,13 +2,10 @@ package setup
 
 import (
 	"context"
+	"io/fs"
 
-	"git.perx.ru/perxis/perxis-go/pkg/clients"
-	"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/roles"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"go.uber.org/zap"
 )
@@ -23,11 +20,6 @@ type Setup struct {
 	SpaceID       string
 	EnvironmentID string
 
-	Roles       []RoleConfig
-	Clients     []ClientConfig
-	Collections []CollectionConfig
-	Items       []ItemConfig
-
 	content *content.Content
 
 	force  bool
@@ -39,6 +31,8 @@ type Setup struct {
 
 	errors []error
 	logger *zap.Logger
+
+	*Config
 }
 
 func NewSetup(content *content.Content, spaceID, environmentID string, logger *zap.Logger) *Setup {
@@ -54,9 +48,23 @@ func NewSetup(content *content.Content, spaceID, environmentID string, logger *z
 		content:            content,
 		logger:             logger,
 		waitSpaceAvailable: true,
+		Config:             NewConfig(), // Копируем конфигурацию, чтобы не изменять исходную в процессе работы
 	}
 }
 
+// Logger возвращает логгер
+func (s *Setup) Logger() *zap.Logger {
+	return s.logger
+}
+
+// WithConfig устанавливает конфигурацию требований к пространству
+// копируем конфигурацию, чтобы не изменять исходную в процессе работы
+func (s *Setup) WithConfig(config *Config) *Setup {
+	setup := *s
+	setup.Config = config.Clone()
+	return &setup
+}
+
 func (s *Setup) WithForce(force bool) *Setup {
 	setup := *s
 	setup.force = force
@@ -99,63 +107,6 @@ func (s *Setup) Error() error {
 	return errors.WithErrors(ErrInvalidSetupConfig, s.errors...)
 }
 
-// AddRoles добавляет требования к настройке ролей в пространстве
-func (s *Setup) AddRoles(roles []*roles.Role, opt ...RolesOption) *Setup {
-	for _, role := range roles {
-		s.AddRole(role, opt...)
-	}
-	return s
-}
-
-func (s *Setup) AddRole(role *roles.Role, opt ...RolesOption) *Setup {
-	s.Roles = append(s.Roles, NewRoleConfig(role, opt...))
-	return s
-}
-
-// AddClients добавляет требования к настройке приложений в пространстве
-func (s *Setup) AddClients(clients []*clients.Client, opt ...ClientsOption) *Setup {
-	for _, client := range clients {
-		s.AddClient(client, opt...)
-	}
-	return s
-}
-
-func (s *Setup) AddClient(client *clients.Client, opt ...ClientsOption) *Setup {
-	s.Clients = append(s.Clients, NewClientConfig(client, opt...))
-	return s
-}
-
-// AddCollections добавляет требования к настройке коллекций в пространстве
-func (s *Setup) AddCollections(collections []*collections.Collection, opt ...CollectionsOption) *Setup {
-	for _, col := range collections {
-		s.AddCollection(col, opt...)
-	}
-	return s
-}
-
-func (s *Setup) AddCollection(collection *collections.Collection, opt ...CollectionsOption) *Setup {
-	config, err := NewCollectionConfig(collection, opt...)
-	if err != nil {
-		s.AddError(err)
-		return s
-	}
-	s.Collections = append(s.Collections, config)
-	return s
-}
-
-// AddItems добавляет требования к настройке элементов в пространстве
-func (s *Setup) AddItems(items []*items.Item, opt ...ItemsOption) *Setup {
-	for _, item := range items {
-		s.AddItem(item, opt...)
-	}
-	return s
-}
-
-func (s *Setup) AddItem(item *items.Item, opt ...ItemsOption) *Setup {
-	s.Items = append(s.Items, NewItemConfig(item, opt...))
-	return s
-}
-
 // Install выполняет установку необходимых требований
 func (s *Setup) Install(ctx context.Context) error {
 	if s.waitSpaceAvailable {
@@ -222,3 +173,12 @@ func (s *Setup) Uninstall(ctx context.Context) error {
 	}
 	return nil
 }
+
+func (s *Setup) Load(fsys fs.FS) *Setup {
+	var err error
+	s.Config, err = s.Config.Load(fsys)
+	if err != nil {
+		s.AddError(err)
+	}
+	return s
+}
diff --git a/pkg/setup/setup_test.go b/pkg/setup/setup_test.go
index 604c2f6b29bae32756e2149ca572ce3b54920936..ff73b745deb0fcec807fa0c0f28b2ba5a0dd7258 100644
--- a/pkg/setup/setup_test.go
+++ b/pkg/setup/setup_test.go
@@ -2,26 +2,27 @@ package setup
 
 import (
 	"context"
+	"errors"
 	"testing"
 
-	"git.perx.ru/perxis/perxis-go/pkg/clients"
 	clientsMock "git.perx.ru/perxis/perxis-go/pkg/clients/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	collectionMock "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/data"
 	environmentMock "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/errors"
-	"git.perx.ru/perxis/perxis-go/pkg/items"
 	itemsMock "git.perx.ru/perxis/perxis-go/pkg/items/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/roles"
 	rolesMock "git.perx.ru/perxis/perxis-go/pkg/roles/mocks"
-	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces/mocks"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	"github.com/stretchr/testify/require"
+
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"git.perx.ru/perxis/perxis-go/pkg/content"
+	"git.perx.ru/perxis/perxis-go/pkg/items"
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"go.uber.org/zap/zaptest"
 )
 
@@ -78,13 +79,14 @@ func getActions() []*items.Item {
 }
 
 func newSetup(content *content.Content, t *testing.T) *Setup {
-	logger := zaptest.NewLogger(t, zaptest.WrapOptions())
-	setup := NewSetup(content, spaceID, envID, logger)
-	setup.AddCollections(getCollections())
-	setup.AddRoles(getRoles())
-	setup.AddClients(getClients())
-	setup.AddItems(getActions(), OverwriteItem())
+	config := NewConfig()
+	config.Collections.AddMany(getCollections())
+	config.Roles.AddMany(getRoles())
+	config.Clients.AddMany(getClients())
+	config.Items.AddMany(getActions(), OverwriteItem())
 
+	logger := zaptest.NewLogger(t, zaptest.WrapOptions())
+	setup := NewSetup(content, spaceID, envID, logger).WithConfig(config)
 	return setup
 }
 
@@ -101,10 +103,8 @@ func TestSetupInstall(t *testing.T) {
 		spcMock.On("Get", mock.Anything, mock.Anything).Return(sps, nil).Once()
 		setup := NewSetup(&content.Content{Spaces: spcMock}, spaceID, envID, logger)
 		err := setup.Install(context.Background())
-
 		require.NoError(t, err)
 		spcMock.AssertExpectations(t)
-
 	})
 
 	t.Run("Success, no force", func(t *testing.T) {
@@ -116,23 +116,19 @@ func TestSetupInstall(t *testing.T) {
 		collsMock := &collectionMock.Collections{}
 		for _, collection := range getCollections() {
 			collsMock.On("Get", mock.Anything, spaceID, envID, collection.ID).
-				Return(nil, collections.ErrNotFound).
-				Once()
+				Return(nil, collections.ErrNotFound).Once()
 
 			collsMock.On("Create", mock.Anything, collection).
-				Return(collection, nil).
-				Once()
+				Return(collection, nil).Once()
 
 			collsMock.On("SetSchema", mock.Anything, spaceID, envID, collection.ID, collection.Schema).
-				Return(nil).
-				Once()
+				Return(nil).Once()
 		}
 
 		rMock := &rolesMock.Roles{}
 		for _, role := range getRoles() {
 			rMock.On("Get", mock.Anything, spaceID, role.ID).
-				Return(nil, roles.ErrNotFound).
-				Once()
+				Return(nil, roles.ErrNotFound).Once()
 
 			rMock.On("Create", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
 				create := args[1].(*roles.Role)
diff --git a/pkg/spaces/errors.go b/pkg/spaces/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..c9e66fa698198eb8b01034eb2bad83286d60297f
--- /dev/null
+++ b/pkg/spaces/errors.go
@@ -0,0 +1,12 @@
+package spaces
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/service"
+)
+
+var (
+	ErrNotFound      = service.ErrNotFound
+	ErrStateConflict = errors.New("state conflicts with current space state")
+	ErrOrgIDRequired = errors.New("organization id required")
+)
diff --git a/pkg/spaces/events.go b/pkg/spaces/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..418e4ea8a27237c39250433f3ff0cfa7c819e457
--- /dev/null
+++ b/pkg/spaces/events.go
@@ -0,0 +1,12 @@
+package spaces
+
+const (
+	EventAbortTransfer = "spaces.abort_transfer"
+	EventCreate        = "spaces.create"
+	EventDelete        = "spaces.delete"
+	EventMove          = "spaces.move"
+	EventTransfer      = "spaces.transfer"
+	EventUpdate        = "spaces.update"
+	EventUpdateConfig  = "spaces.update_config"
+	EventMigrate       = "spaces.migrate"
+)
diff --git a/pkg/spaces/middleware/access_logging_middleware.go b/pkg/spaces/middleware/access_logging_middleware.go
index 7fca51ede8a87ba31ceb7293537e32c2db4c08d7..9596d7d4fc2daa611a0db631eef56fe6d5531488 100644
--- a/pkg/spaces/middleware/access_logging_middleware.go
+++ b/pkg/spaces/middleware/access_logging_middleware.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"git.perx.ru/perxis/perxis-go/pkg/auth"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"go.uber.org/zap"
 )
@@ -86,6 +87,27 @@ func (m *accessLoggingMiddleware) Delete(ctx context.Context, spaceId string) (e
 	return err
 }
 
+func (m *accessLoggingMiddleware) Find(ctx context.Context, filter *spaces.Filter, fo *options.FindOptions) (spaces []*spaces.Space, total int, err error) {
+	begin := time.Now()
+
+	m.logger.Debug("Find.Request",
+		zap.Reflect("principal", auth.GetPrincipal(ctx)),
+		zap.Reflect("filter", filter),
+		zap.Reflect("fo", fo),
+	)
+
+	spaces, total, err = m.next.Find(ctx, filter, fo)
+
+	m.logger.Debug("Find.Response",
+		zap.Duration("time", time.Since(begin)),
+		zap.Reflect("spaces", spaces),
+		zap.Reflect("total", total),
+		zap.Error(err),
+	)
+
+	return spaces, total, err
+}
+
 func (m *accessLoggingMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) {
 	begin := time.Now()
 
@@ -217,3 +239,22 @@ func (m *accessLoggingMiddleware) UpdateConfig(ctx context.Context, spaceId stri
 
 	return err
 }
+
+func (m *accessLoggingMiddleware) SetState(ctx context.Context, spaceID string, state *spaces.StateInfo) (err error) {
+	begin := time.Now()
+
+	m.logger.Debug("SetState.Request",
+		zap.Reflect("principal", auth.GetPrincipal(ctx)),
+		zap.Reflect("spaceID", spaceID),
+		zap.Reflect("state", state),
+	)
+
+	err = m.next.SetState(ctx, spaceID, state)
+
+	m.logger.Debug("SetState.Response",
+		zap.Duration("time", time.Since(begin)),
+		zap.Error(err),
+	)
+
+	return err
+}
diff --git a/pkg/spaces/middleware/caching_middleware.go b/pkg/spaces/middleware/caching_middleware.go
index f99e21a1f4a254ef6fb0890d09776b9b4738fd38..c7f35de12106e9aa828e56ebd6f6ec4361c70da7 100644
--- a/pkg/spaces/middleware/caching_middleware.go
+++ b/pkg/spaces/middleware/caching_middleware.go
@@ -4,6 +4,8 @@ import (
 	"context"
 
 	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
 	service "git.perx.ru/perxis/perxis-go/pkg/spaces"
 )
 
@@ -29,7 +31,7 @@ func (m cachingMiddleware) Create(ctx context.Context, space *service.Space) (sp
 
 	sp, err = m.next.Create(ctx, space)
 	if err == nil {
-		m.cache.Remove(orgKey(sp.OrgID))
+		_ = m.cache.Remove(orgKey(sp.OrgID))
 	}
 	return sp, err
 }
@@ -38,29 +40,35 @@ func (m cachingMiddleware) Get(ctx context.Context, spaceId string) (sp *service
 
 	value, e := m.cache.Get(spaceId)
 	if e == nil {
-		return value.(*service.Space), err
+		return value.(*service.Space).Clone(), nil
 	}
 	sp, err = m.next.Get(ctx, spaceId)
 	if err == nil {
-		m.cache.Set(spaceId, sp)
+		_ = m.cache.Set(spaceId, sp)
+		return sp.Clone(), nil
 	}
-	return sp, err
+	return nil, err
 }
 
 func (m cachingMiddleware) List(ctx context.Context, orgId string) (spaces []*service.Space, err error) {
 
 	value, e := m.cache.Get(orgKey(orgId))
 	if e == nil {
-		return value.([]*service.Space), err
+		return data.CloneSlice(value.([]*service.Space)), nil
 	}
 	spaces, err = m.next.List(ctx, orgId)
 	if err == nil {
-		m.cache.Set(orgKey(orgId), spaces)
+		_ = m.cache.Set(orgKey(orgId), spaces)
 		for _, s := range spaces {
-			m.cache.Set(s.ID, s)
+			_ = m.cache.Set(s.ID, s)
 		}
+		return data.CloneSlice(spaces), nil
 	}
-	return spaces, err
+	return nil, err
+}
+
+func (m cachingMiddleware) Find(ctx context.Context, filter *service.Filter, fo *options.FindOptions) (spaces []*service.Space, total int, err error) {
+	return m.next.Find(ctx, filter, fo)
 }
 
 func (m cachingMiddleware) Update(ctx context.Context, space *service.Space) (err error) {
@@ -70,9 +78,9 @@ func (m cachingMiddleware) Update(ctx context.Context, space *service.Space) (er
 		value, e := m.cache.Get(space.ID)
 		if e == nil {
 			space := value.(*service.Space)
-			m.cache.Remove(orgKey(space.OrgID))
+			_ = m.cache.Remove(orgKey(space.OrgID))
 		}
-		m.cache.Remove(space.ID)
+		_ = m.cache.Remove(space.ID)
 	}
 	return err
 }
@@ -84,9 +92,23 @@ func (m cachingMiddleware) UpdateConfig(ctx context.Context, spaceId string, con
 		value, e := m.cache.Get(spaceId)
 		if e == nil {
 			space := value.(*service.Space)
-			m.cache.Remove(orgKey(space.OrgID))
+			_ = m.cache.Remove(orgKey(space.OrgID))
+		}
+		_ = m.cache.Remove(spaceId)
+	}
+	return err
+}
+
+func (m cachingMiddleware) SetState(ctx context.Context, spaceID string, state *service.StateInfo) (err error) {
+
+	err = m.next.SetState(ctx, spaceID, state)
+	if err == nil {
+		value, e := m.cache.Get(spaceID)
+		if e == nil {
+			space := value.(*service.Space)
+			_ = m.cache.Remove(orgKey(space.OrgID))
 		}
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceID)
 	}
 	return err
 }
@@ -98,9 +120,9 @@ func (m cachingMiddleware) Delete(ctx context.Context, spaceId string) (err erro
 		value, e := m.cache.Get(spaceId)
 		if e == nil {
 			space := value.(*service.Space)
-			m.cache.Remove(orgKey(space.OrgID))
+			_ = m.cache.Remove(orgKey(space.OrgID))
 		}
-		m.cache.Remove(spaceId)
+		_ = m.cache.Remove(spaceId)
 	}
 	return err
 }
@@ -111,11 +133,11 @@ func (m cachingMiddleware) Transfer(ctx context.Context, spaceID, transferToOrg
 		value, e := m.cache.Get(spaceID)
 		if e == nil {
 			space := value.(*service.Space)
-			m.cache.Remove(orgKey(space.OrgID))
-			m.cache.Remove(orgKey(space.TransferToOrg))
+			_ = m.cache.Remove(orgKey(space.OrgID))
+			_ = m.cache.Remove(orgKey(space.TransferToOrg))
 		}
-		m.cache.Remove(spaceID)
-		m.cache.Remove(transferToOrg)
+		_ = m.cache.Remove(spaceID)
+		_ = m.cache.Remove(transferToOrg)
 	}
 	return err
 }
@@ -126,10 +148,10 @@ func (m cachingMiddleware) AbortTransfer(ctx context.Context, spaceID string) er
 		value, e := m.cache.Get(spaceID)
 		if e == nil {
 			space := value.(*service.Space)
-			m.cache.Remove(orgKey(space.OrgID))
-			m.cache.Remove(orgKey(space.TransferToOrg))
+			_ = m.cache.Remove(orgKey(space.OrgID))
+			_ = m.cache.Remove(orgKey(space.TransferToOrg))
 		}
-		m.cache.Remove(spaceID)
+		_ = m.cache.Remove(spaceID)
 	}
 	return err
 }
@@ -144,11 +166,11 @@ func (m cachingMiddleware) Move(ctx context.Context, spaceID, orgID string) erro
 		value, e := m.cache.Get(spaceID)
 		if e == nil {
 			space := value.(*service.Space)
-			m.cache.Remove(orgKey(space.OrgID))
-			m.cache.Remove(orgKey(space.TransferToOrg))
+			_ = m.cache.Remove(orgKey(space.OrgID))
+			_ = m.cache.Remove(orgKey(space.TransferToOrg))
 		}
-		m.cache.Remove(spaceID)
-		m.cache.Remove(orgID)
+		_ = m.cache.Remove(spaceID)
+		_ = m.cache.Remove(orgID)
 	}
 	return err
 }
diff --git a/pkg/spaces/middleware/caching_middleware_test.go b/pkg/spaces/middleware/caching_middleware_test.go
index e87ceb38c79c6b15d76ab6f6ff8f628a680a5aa6..0674c932eb4e5f9d519a587a45cc4c5003b8f7d7 100644
--- a/pkg/spaces/middleware/caching_middleware_test.go
+++ b/pkg/spaces/middleware/caching_middleware_test.go
@@ -40,7 +40,8 @@ func TestRolesCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, spaceID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+		assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+		assert.NotSame(t, v1, v2)
 
 		sp.AssertExpectations(t)
 	})
@@ -57,7 +58,8 @@ func TestRolesCache(t *testing.T) {
 
 		vl2, err := svc.List(ctx, orgID)
 		require.NoError(t, err)
-		assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+		assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+		assert.NotSame(t, vl1[0], vl2[0])
 
 		sp.AssertExpectations(t)
 	})
@@ -76,14 +78,16 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			vl1, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			sp.On("Update", mock.Anything, mock.Anything).Return(nil).Once()
 
@@ -95,11 +99,11 @@ func TestRolesCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v3, "Ожидается что кеш объекта был удален после обновления объекта.")
+			assert.NotEqual(t, v2, v3, "Ожидается что кеш объекта был удален после обновления объекта.")
 
 			vl3, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
 
 			sp.AssertExpectations(t)
 		})
@@ -117,14 +121,16 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			vl1, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			sp.On("UpdateConfig", mock.Anything, spaceID, mock.Anything).Return(nil).Once()
 
@@ -136,11 +142,11 @@ func TestRolesCache(t *testing.T) {
 
 			v3, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
-			assert.NotSame(t, v2, v3, "Ожидается что кеш объекта был удален после обновления объекта.")
+			assert.NotEqual(t, v2, v3, "Ожидается что кеш объекта был удален после обновления объекта.")
 
 			vl3, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
-			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
+			assert.NotEqual(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после обновления объекта.")
 
 			sp.AssertExpectations(t)
 		})
@@ -158,14 +164,16 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			vl1, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
 
 			vl2, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			sp.On("Delete", mock.Anything, spaceID).Return(nil).Once()
 
@@ -198,7 +206,8 @@ func TestRolesCache(t *testing.T) {
 
 			vl2, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
-			assert.Same(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.Equal(t, vl1[0], vl2[0], "Ожидается при повторном запросе получение объектов из кэша.")
+			assert.NotSame(t, vl1[0], vl2[0])
 
 			sp.On("Create", mock.Anything, mock.Anything).Return(&spaces.Space{ID: "spaceID2", OrgID: orgID, Name: "Space2"}, nil).Once()
 
@@ -210,6 +219,8 @@ func TestRolesCache(t *testing.T) {
 			vl3, err := svc.List(ctx, orgID)
 			require.NoError(t, err)
 			assert.NotSame(t, vl2[0], vl3[0], "Ожидается что кеш объектов был удален после создания нового объекта.")
+			assert.Equal(t, vl2[0], vl3[0])
+			assert.NotSame(t, vl2[0], vl3[0])
 
 			sp.AssertExpectations(t)
 		})
@@ -226,7 +237,8 @@ func TestRolesCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается при повторном запросе получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 			sp.On("Get", mock.Anything, spaceID).Return(&spaces.Space{ID: spaceID, OrgID: orgID, Name: "Space"}, nil).Once()
@@ -234,6 +246,8 @@ func TestRolesCache(t *testing.T) {
 			v3, err := svc.Get(ctx, spaceID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается удаление объекта из кэша по истечению ttl.")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			sp.AssertExpectations(t)
 		})
diff --git a/pkg/spaces/middleware/error_logging_middleware.go b/pkg/spaces/middleware/error_logging_middleware.go
index 2b11838b51f1a033e9963a729c6378401e193d0c..677b48366b6304e47988578bcb694ad758ba3ab0 100644
--- a/pkg/spaces/middleware/error_logging_middleware.go
+++ b/pkg/spaces/middleware/error_logging_middleware.go
@@ -9,6 +9,7 @@ package middleware
 import (
 	"context"
 
+	"git.perx.ru/perxis/perxis-go/pkg/options"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"go.uber.org/zap"
 )
@@ -59,6 +60,16 @@ func (m *errorLoggingMiddleware) Delete(ctx context.Context, spaceId string) (er
 	return m.next.Delete(ctx, spaceId)
 }
 
+func (m *errorLoggingMiddleware) Find(ctx context.Context, filter *spaces.Filter, fo *options.FindOptions) (spaces []*spaces.Space, total int, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Find(ctx, filter, fo)
+}
+
 func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) {
 	logger := m.logger
 	defer func() {
@@ -99,6 +110,16 @@ func (m *errorLoggingMiddleware) Move(ctx context.Context, spaceID string, orgID
 	return m.next.Move(ctx, spaceID, orgID)
 }
 
+func (m *errorLoggingMiddleware) SetState(ctx context.Context, spaceID string, state *spaces.StateInfo) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.SetState(ctx, spaceID, state)
+}
+
 func (m *errorLoggingMiddleware) Transfer(ctx context.Context, spaceID string, transferToOrg string) (err error) {
 	logger := m.logger
 	defer func() {
diff --git a/pkg/spaces/middleware/logging_middleware.go b/pkg/spaces/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..a45ec2df50923d480f9c94f3c2f26bf266121aff
--- /dev/null
+++ b/pkg/spaces/middleware/logging_middleware.go
@@ -0,0 +1,236 @@
+package middleware
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"git.perx.ru/perxis/perxis-go/pkg/spaces"
+
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   spaces.Spaces
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next spaces.Spaces) spaces.Spaces {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Spaces")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) AbortTransfer(ctx context.Context, spaceID string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventAbortTransfer),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	err = m.next.AbortTransfer(ctx, spaceID)
+	if err != nil {
+		logger.Error("Failed to abort transfer", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Aborted space transfer", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, space *spaces.Space) (created *spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventCreate),
+	)
+
+	created, err = m.next.Create(ctx, space)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(space))
+		return
+	}
+
+	logger.Info("Space created", logzap.Channels(logzap.Userlog), logzap.Object(created))
+
+	return created, err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string) (err error) {
+	space, err := m.Get(ctx, spaceId)
+	if err != nil {
+		return err
+	}
+
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventDelete),
+		logzap.Object(id.NewOrganizationId(space.OrgID)),
+	)
+
+	err = m.next.Delete(ctx, spaceId)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to delete space '%s'", spaceId), zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Space '%s' deleted", spaceId), logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	space, err = m.next.Get(ctx, spaceId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return space, err
+}
+
+func (m *loggingMiddleware) List(ctx context.Context, orgId string) (spaces []*spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, err = m.next.List(ctx, orgId)
+	if err != nil {
+		logger.Error("Failed to list", zap.Error(err))
+		return
+	}
+
+	return spaces, err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *spaces.Filter, fo *options.FindOptions) (spaces []*spaces.Space, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, total, err = m.next.Find(ctx, filter, fo)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return spaces, total, err
+}
+
+func (m *loggingMiddleware) ListTransfers(ctx context.Context, orgID string) (spaces []*spaces.Space, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	spaces, err = m.next.ListTransfers(ctx, orgID)
+	if err != nil {
+		logger.Error("Failed to list transfers", zap.Error(err))
+		return
+	}
+
+	return spaces, err
+}
+
+func (m *loggingMiddleware) Move(ctx context.Context, spaceID string, orgID string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventMove),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	err = m.next.Move(ctx, spaceID, orgID)
+	if err != nil {
+		logger.Error("Failed to move", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space moved", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Transfer(ctx context.Context, spaceID string, transferToOrg string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventTransfer),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	err = m.next.Transfer(ctx, spaceID, transferToOrg)
+	if err != nil {
+		logger.Error("Failed to transfer", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space transferred", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, space *spaces.Space) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventUpdate),
+		logzap.Object(space),
+	)
+
+	err = m.next.Update(ctx, space)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Space updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) SetState(ctx context.Context, spaceID string, state *spaces.StateInfo) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventUpdate),
+		logzap.Object(id.NewSpaceId(spaceID)),
+	)
+
+	var st spaces.State
+	if state != nil {
+		st = state.State
+	}
+
+	err = m.next.SetState(ctx, spaceID, state)
+	if err != nil {
+		logger.Error(fmt.Sprintf("Failed to set state '%s' to space", st), zap.Error(err))
+		return
+	}
+
+	logger.Info(fmt.Sprintf("Set state '%s' to space", st))
+	return err
+}
+
+func (m *loggingMiddleware) UpdateConfig(ctx context.Context, spaceId string, config *spaces.Config) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(spaces.EventUpdateConfig),
+		logzap.Object(id.NewSpaceId(spaceId)),
+	)
+
+	err = m.next.UpdateConfig(ctx, spaceId, config)
+	if err != nil {
+		logger.Error("Failed to update config", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("Updated space config", logzap.Channels(logzap.Userlog))
+
+	return err
+}
diff --git a/pkg/spaces/middleware/middleware.go b/pkg/spaces/middleware/middleware.go
index 73c3b8c3538e6bf9a6457617afc35f68f429aaf3..15f2bc259beacd5ea312632ba042a33b59022f32 100644
--- a/pkg/spaces/middleware/middleware.go
+++ b/pkg/spaces/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s spaces.Spaces, logger *zap.Logger, log_access bool) spaces.Spaces
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/spaces/middleware/recovering_middleware.go b/pkg/spaces/middleware/recovering_middleware.go
index 7b9b64ed276aa9a91dd9e07ee1b463ff8bb06003..cca581b6643b32bf21f31964aac200bb9613515d 100644
--- a/pkg/spaces/middleware/recovering_middleware.go
+++ b/pkg/spaces/middleware/recovering_middleware.go
@@ -10,6 +10,7 @@ import (
 	"context"
 	"fmt"
 
+	"git.perx.ru/perxis/perxis-go/pkg/options"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"go.uber.org/zap"
 )
@@ -66,6 +67,18 @@ func (m *recoveringMiddleware) Delete(ctx context.Context, spaceId string) (err
 	return m.next.Delete(ctx, spaceId)
 }
 
+func (m *recoveringMiddleware) Find(ctx context.Context, filter *spaces.Filter, fo *options.FindOptions) (spaces []*spaces.Space, total int, 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.Find(ctx, filter, fo)
+}
+
 func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) {
 	logger := m.logger
 	defer func() {
@@ -114,6 +127,18 @@ func (m *recoveringMiddleware) Move(ctx context.Context, spaceID string, orgID s
 	return m.next.Move(ctx, spaceID, orgID)
 }
 
+func (m *recoveringMiddleware) SetState(ctx context.Context, spaceID string, state *spaces.StateInfo) (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.SetState(ctx, spaceID, state)
+}
+
 func (m *recoveringMiddleware) Transfer(ctx context.Context, spaceID string, transferToOrg string) (err error) {
 	logger := m.logger
 	defer func() {
diff --git a/pkg/spaces/middleware/telemetry_middleware.go b/pkg/spaces/middleware/telemetry_middleware.go
index 90597815e8e7aa0a3a364c3e29aa208f3473d690..e9623692afe644e58cb1b406b4b06a3208edc710 100644
--- a/pkg/spaces/middleware/telemetry_middleware.go
+++ b/pkg/spaces/middleware/telemetry_middleware.go
@@ -1,10 +1,10 @@
 // Code generated by gowrap. DO NOT EDIT.
-// template: ..\..\..\assets\templates\middleware\telemetry
+// template: ../../../assets/templates/middleware/telemetry
 // gowrap: http://github.com/hexdigest/gowrap
 
 package middleware
 
-//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/spaces -i Spaces -t ..\..\..\assets\templates\middleware\telemetry -o telemetry_middleware.go -l ""
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/spaces -i Spaces -t ../../../assets/templates/middleware/telemetry -o telemetry_middleware.go -l ""
 
 // source template: https://github.com/hexdigest/gowrap/blob/master/templates/opentelemetry
 
@@ -12,6 +12,7 @@ import (
 	"context"
 	"time"
 
+	"git.perx.ru/perxis/perxis-go/pkg/options"
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 	"git.perx.ru/perxis/perxis-go/pkg/telemetry/metrics"
 	"go.opentelemetry.io/otel"
@@ -148,6 +149,42 @@ func (_d telemetryMiddleware) Delete(ctx context.Context, spaceId string) (err e
 	return _d.Spaces.Delete(ctx, spaceId)
 }
 
+// Find implements spaces.Spaces
+func (_d telemetryMiddleware) Find(ctx context.Context, filter *spaces.Filter, fo *options.FindOptions) (spaces []*spaces.Space, total int, err error) {
+	attributes := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "Find"),
+	))
+
+	_d.requestMetrics.Total.Add(ctx, 1, attributes)
+
+	start := time.Now()
+	ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.Find")
+
+	defer func() {
+		_d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes)
+
+		if _d._spanDecorator != nil {
+			_d._spanDecorator(_span, map[string]interface{}{
+				"ctx":    ctx,
+				"filter": filter,
+				"fo":     fo}, map[string]interface{}{
+				"spaces": spaces,
+				"total":  total,
+				"err":    err})
+		} else if err != nil {
+			_d.requestMetrics.FailedTotal.Add(ctx, 1, attributes)
+
+			_span.RecordError(err)
+			_span.SetAttributes(attribute.String("event", "error"))
+			_span.SetAttributes(attribute.String("message", err.Error()))
+		}
+
+		_span.End()
+	}()
+	return _d.Spaces.Find(ctx, filter, fo)
+}
+
 // Get implements spaces.Spaces
 func (_d telemetryMiddleware) Get(ctx context.Context, spaceId string) (space *spaces.Space, err error) {
 	attributes := otelmetric.WithAttributeSet(attribute.NewSet(
@@ -284,6 +321,40 @@ func (_d telemetryMiddleware) Move(ctx context.Context, spaceID string, orgID st
 	return _d.Spaces.Move(ctx, spaceID, orgID)
 }
 
+// SetState implements spaces.Spaces
+func (_d telemetryMiddleware) SetState(ctx context.Context, spaceID string, state *spaces.StateInfo) (err error) {
+	attributes := otelmetric.WithAttributeSet(attribute.NewSet(
+		attribute.String("service", "Spaces"),
+		attribute.String("method", "SetState"),
+	))
+
+	_d.requestMetrics.Total.Add(ctx, 1, attributes)
+
+	start := time.Now()
+	ctx, _span := otel.Tracer(_d._instance).Start(ctx, "Spaces.SetState")
+
+	defer func() {
+		_d.requestMetrics.DurationMilliseconds.Record(ctx, time.Since(start).Milliseconds(), attributes)
+
+		if _d._spanDecorator != nil {
+			_d._spanDecorator(_span, map[string]interface{}{
+				"ctx":     ctx,
+				"spaceID": spaceID,
+				"state":   state}, map[string]interface{}{
+				"err": err})
+		} else if err != nil {
+			_d.requestMetrics.FailedTotal.Add(ctx, 1, attributes)
+
+			_span.RecordError(err)
+			_span.SetAttributes(attribute.String("event", "error"))
+			_span.SetAttributes(attribute.String("message", err.Error()))
+		}
+
+		_span.End()
+	}()
+	return _d.Spaces.SetState(ctx, spaceID, state)
+}
+
 // Transfer implements spaces.Spaces
 func (_d telemetryMiddleware) Transfer(ctx context.Context, spaceID string, transferToOrg string) (err error) {
 	attributes := otelmetric.WithAttributeSet(attribute.NewSet(
diff --git a/pkg/spaces/mocks/SpaceCreatedObserver.go b/pkg/spaces/mocks/SpaceCreatedObserver.go
index a85eced793564cc3fc27304881486329041c3d75..049a741a757f5d107d347f826363eafeb29d85ac 100644
--- a/pkg/spaces/mocks/SpaceCreatedObserver.go
+++ b/pkg/spaces/mocks/SpaceCreatedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.40.3. DO NOT EDIT.
 
 package mocks
 
@@ -18,6 +18,10 @@ type SpaceCreatedObserver struct {
 func (_m *SpaceCreatedObserver) OnSpaceCreated(ctx context.Context, space *spaces.Space) error {
 	ret := _m.Called(ctx, space)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnSpaceCreated")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) error); ok {
 		r0 = rf(ctx, space)
@@ -28,13 +32,12 @@ func (_m *SpaceCreatedObserver) OnSpaceCreated(ctx context.Context, space *space
 	return r0
 }
 
-type mockConstructorTestingTNewSpaceCreatedObserver interface {
+// NewSpaceCreatedObserver creates a new instance of SpaceCreatedObserver. 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 NewSpaceCreatedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewSpaceCreatedObserver creates a new instance of SpaceCreatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewSpaceCreatedObserver(t mockConstructorTestingTNewSpaceCreatedObserver) *SpaceCreatedObserver {
+}) *SpaceCreatedObserver {
 	mock := &SpaceCreatedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/spaces/mocks/SpaceDeletedObserver.go b/pkg/spaces/mocks/SpaceDeletedObserver.go
index 450173b03b0007c86b4dffee69a0c36522f9ce28..d4e65da6af20b85ab56159fcafed103f92b9c26b 100644
--- a/pkg/spaces/mocks/SpaceDeletedObserver.go
+++ b/pkg/spaces/mocks/SpaceDeletedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.40.3. DO NOT EDIT.
 
 package mocks
 
@@ -18,6 +18,10 @@ type SpaceDeletedObserver struct {
 func (_m *SpaceDeletedObserver) OnSpaceDeleted(ctx context.Context, space *spaces.Space) error {
 	ret := _m.Called(ctx, space)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnSpaceDeleted")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) error); ok {
 		r0 = rf(ctx, space)
@@ -28,13 +32,12 @@ func (_m *SpaceDeletedObserver) OnSpaceDeleted(ctx context.Context, space *space
 	return r0
 }
 
-type mockConstructorTestingTNewSpaceDeletedObserver interface {
+// NewSpaceDeletedObserver creates a new instance of SpaceDeletedObserver. 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 NewSpaceDeletedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewSpaceDeletedObserver creates a new instance of SpaceDeletedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewSpaceDeletedObserver(t mockConstructorTestingTNewSpaceDeletedObserver) *SpaceDeletedObserver {
+}) *SpaceDeletedObserver {
 	mock := &SpaceDeletedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/spaces/mocks/SpaceObserver.go b/pkg/spaces/mocks/SpaceObserver.go
index 9c5006bf80047884f45637207d3ea6fdf0b702aa..589a7755a4cd86c025c816d652081e0597720d5e 100644
--- a/pkg/spaces/mocks/SpaceObserver.go
+++ b/pkg/spaces/mocks/SpaceObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.40.3. DO NOT EDIT.
 
 package mocks
 
@@ -9,13 +9,12 @@ type SpaceObserver struct {
 	mock.Mock
 }
 
-type mockConstructorTestingTNewSpaceObserver interface {
+// NewSpaceObserver creates a new instance of SpaceObserver. 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 NewSpaceObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewSpaceObserver creates a new instance of SpaceObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewSpaceObserver(t mockConstructorTestingTNewSpaceObserver) *SpaceObserver {
+}) *SpaceObserver {
 	mock := &SpaceObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/spaces/mocks/SpaceUpdatedObserver.go b/pkg/spaces/mocks/SpaceUpdatedObserver.go
index a7ced9b2d7bab0618013b5f73cef304c40cecb57..adfa9ea58992d08746ae6072743fe59a21a4bd10 100644
--- a/pkg/spaces/mocks/SpaceUpdatedObserver.go
+++ b/pkg/spaces/mocks/SpaceUpdatedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.40.3. DO NOT EDIT.
 
 package mocks
 
@@ -18,6 +18,10 @@ type SpaceUpdatedObserver struct {
 func (_m *SpaceUpdatedObserver) OnSpaceUpdated(ctx context.Context, before *spaces.Space, space *spaces.Space) error {
 	ret := _m.Called(ctx, before, space)
 
+	if len(ret) == 0 {
+		panic("no return value specified for OnSpaceUpdated")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space, *spaces.Space) error); ok {
 		r0 = rf(ctx, before, space)
@@ -28,13 +32,12 @@ func (_m *SpaceUpdatedObserver) OnSpaceUpdated(ctx context.Context, before *spac
 	return r0
 }
 
-type mockConstructorTestingTNewSpaceUpdatedObserver interface {
+// NewSpaceUpdatedObserver creates a new instance of SpaceUpdatedObserver. 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 NewSpaceUpdatedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewSpaceUpdatedObserver creates a new instance of SpaceUpdatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewSpaceUpdatedObserver(t mockConstructorTestingTNewSpaceUpdatedObserver) *SpaceUpdatedObserver {
+}) *SpaceUpdatedObserver {
 	mock := &SpaceUpdatedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/spaces/mocks/Spaces.go b/pkg/spaces/mocks/Spaces.go
index 0f7a187f31a572c96205427dae3438aac768c5e5..76121895f428dbcd8aa8117f4a8e376845d81e0b 100644
--- a/pkg/spaces/mocks/Spaces.go
+++ b/pkg/spaces/mocks/Spaces.go
@@ -1,12 +1,14 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.40.3. DO NOT EDIT.
 
 package mocks
 
 import (
 	context "context"
 
-	spaces "git.perx.ru/perxis/perxis-go/pkg/spaces"
+	options "git.perx.ru/perxis/perxis-go/pkg/options"
 	mock "github.com/stretchr/testify/mock"
+
+	spaces "git.perx.ru/perxis/perxis-go/pkg/spaces"
 )
 
 // Spaces is an autogenerated mock type for the Spaces type
@@ -18,6 +20,10 @@ type Spaces struct {
 func (_m *Spaces) AbortTransfer(ctx context.Context, spaceID string) error {
 	ret := _m.Called(ctx, spaceID)
 
+	if len(ret) == 0 {
+		panic("no return value specified for AbortTransfer")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
 		r0 = rf(ctx, spaceID)
@@ -32,6 +38,10 @@ func (_m *Spaces) AbortTransfer(ctx context.Context, spaceID string) error {
 func (_m *Spaces) Create(ctx context.Context, space *spaces.Space) (*spaces.Space, error) {
 	ret := _m.Called(ctx, space)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Create")
+	}
+
 	var r0 *spaces.Space
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) (*spaces.Space, error)); ok {
@@ -58,6 +68,10 @@ func (_m *Spaces) Create(ctx context.Context, space *spaces.Space) (*spaces.Spac
 func (_m *Spaces) Delete(ctx context.Context, spaceId string) error {
 	ret := _m.Called(ctx, spaceId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Delete")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
 		r0 = rf(ctx, spaceId)
@@ -68,10 +82,51 @@ func (_m *Spaces) Delete(ctx context.Context, spaceId string) error {
 	return r0
 }
 
+// Find provides a mock function with given fields: ctx, filter, fo
+func (_m *Spaces) Find(ctx context.Context, filter *spaces.Filter, fo *options.FindOptions) ([]*spaces.Space, int, error) {
+	ret := _m.Called(ctx, filter, fo)
+
+	if len(ret) == 0 {
+		panic("no return value specified for Find")
+	}
+
+	var r0 []*spaces.Space
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Filter, *options.FindOptions) ([]*spaces.Space, int, error)); ok {
+		return rf(ctx, filter, fo)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Filter, *options.FindOptions) []*spaces.Space); ok {
+		r0 = rf(ctx, filter, fo)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*spaces.Space)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.Filter, *options.FindOptions) int); ok {
+		r1 = rf(ctx, filter, fo)
+	} else {
+		r1 = ret.Get(1).(int)
+	}
+
+	if rf, ok := ret.Get(2).(func(context.Context, *spaces.Filter, *options.FindOptions) error); ok {
+		r2 = rf(ctx, filter, fo)
+	} else {
+		r2 = ret.Error(2)
+	}
+
+	return r0, r1, r2
+}
+
 // Get provides a mock function with given fields: ctx, spaceId
 func (_m *Spaces) Get(ctx context.Context, spaceId string) (*spaces.Space, error) {
 	ret := _m.Called(ctx, spaceId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Get")
+	}
+
 	var r0 *spaces.Space
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string) (*spaces.Space, error)); ok {
@@ -98,6 +153,10 @@ func (_m *Spaces) Get(ctx context.Context, spaceId string) (*spaces.Space, error
 func (_m *Spaces) List(ctx context.Context, orgId string) ([]*spaces.Space, error) {
 	ret := _m.Called(ctx, orgId)
 
+	if len(ret) == 0 {
+		panic("no return value specified for List")
+	}
+
 	var r0 []*spaces.Space
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*spaces.Space, error)); ok {
@@ -124,6 +183,10 @@ func (_m *Spaces) List(ctx context.Context, orgId string) ([]*spaces.Space, erro
 func (_m *Spaces) ListTransfers(ctx context.Context, orgID string) ([]*spaces.Space, error) {
 	ret := _m.Called(ctx, orgID)
 
+	if len(ret) == 0 {
+		panic("no return value specified for ListTransfers")
+	}
+
 	var r0 []*spaces.Space
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*spaces.Space, error)); ok {
@@ -150,6 +213,10 @@ func (_m *Spaces) ListTransfers(ctx context.Context, orgID string) ([]*spaces.Sp
 func (_m *Spaces) Move(ctx context.Context, spaceID string, orgID string) error {
 	ret := _m.Called(ctx, spaceID, orgID)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Move")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
 		r0 = rf(ctx, spaceID, orgID)
@@ -160,10 +227,32 @@ func (_m *Spaces) Move(ctx context.Context, spaceID string, orgID string) error
 	return r0
 }
 
+// SetState provides a mock function with given fields: ctx, spaceID, state
+func (_m *Spaces) SetState(ctx context.Context, spaceID string, state *spaces.StateInfo) error {
+	ret := _m.Called(ctx, spaceID, state)
+
+	if len(ret) == 0 {
+		panic("no return value specified for SetState")
+	}
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, *spaces.StateInfo) error); ok {
+		r0 = rf(ctx, spaceID, state)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
 // Transfer provides a mock function with given fields: ctx, spaceID, transferToOrg
 func (_m *Spaces) Transfer(ctx context.Context, spaceID string, transferToOrg string) error {
 	ret := _m.Called(ctx, spaceID, transferToOrg)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Transfer")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
 		r0 = rf(ctx, spaceID, transferToOrg)
@@ -178,6 +267,10 @@ func (_m *Spaces) Transfer(ctx context.Context, spaceID string, transferToOrg st
 func (_m *Spaces) Update(ctx context.Context, space *spaces.Space) error {
 	ret := _m.Called(ctx, space)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Update")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) error); ok {
 		r0 = rf(ctx, space)
@@ -192,6 +285,10 @@ func (_m *Spaces) Update(ctx context.Context, space *spaces.Space) error {
 func (_m *Spaces) UpdateConfig(ctx context.Context, spaceId string, config *spaces.Config) error {
 	ret := _m.Called(ctx, spaceId, config)
 
+	if len(ret) == 0 {
+		panic("no return value specified for UpdateConfig")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, string, *spaces.Config) error); ok {
 		r0 = rf(ctx, spaceId, config)
@@ -202,13 +299,12 @@ func (_m *Spaces) UpdateConfig(ctx context.Context, spaceId string, config *spac
 	return r0
 }
 
-type mockConstructorTestingTNewSpaces interface {
+// NewSpaces creates a new instance of Spaces. 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 NewSpaces(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewSpaces creates a new instance of Spaces. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewSpaces(t mockConstructorTestingTNewSpaces) *Spaces {
+}) *Spaces {
 	mock := &Spaces{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/spaces/mocks/Storage.go b/pkg/spaces/mocks/Storage.go
index 0f61e8c4d5796032d6e6103b070512dc8c5b87b7..078993cebec0cb2a5748c0d5bf1c50fb68d4def0 100644
--- a/pkg/spaces/mocks/Storage.go
+++ b/pkg/spaces/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.40.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,6 +20,10 @@ type Storage struct {
 func (_m *Storage) Create(ctx context.Context, space *spaces.Space) error {
 	ret := _m.Called(ctx, space)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Create")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Space) error); ok {
 		r0 = rf(ctx, space)
@@ -34,6 +38,10 @@ func (_m *Storage) Create(ctx context.Context, space *spaces.Space) error {
 func (_m *Storage) Delete(ctx context.Context, filter *spaces.Filter) (int, error) {
 	ret := _m.Called(ctx, filter)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Delete")
+	}
+
 	var r0 int
 	var r1 error
 	if rf, ok := ret.Get(0).(func(context.Context, *spaces.Filter) (int, error)); ok {
@@ -58,6 +66,10 @@ func (_m *Storage) Delete(ctx context.Context, filter *spaces.Filter) (int, erro
 func (_m *Storage) Find(ctx context.Context, filter *spaces.Filter, opts *options.FindOptions) ([]*spaces.Space, int, error) {
 	ret := _m.Called(ctx, filter, opts)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Find")
+	}
+
 	var r0 []*spaces.Space
 	var r1 int
 	var r2 error
@@ -91,6 +103,10 @@ func (_m *Storage) Find(ctx context.Context, filter *spaces.Filter, opts *option
 func (_m *Storage) Init(ctx context.Context) error {
 	ret := _m.Called(ctx)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Init")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context) error); ok {
 		r0 = rf(ctx)
@@ -105,6 +121,10 @@ func (_m *Storage) Init(ctx context.Context) error {
 func (_m *Storage) Reset(ctx context.Context) error {
 	ret := _m.Called(ctx)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Reset")
+	}
+
 	var r0 error
 	if rf, ok := ret.Get(0).(func(context.Context) error); ok {
 		r0 = rf(ctx)
@@ -119,6 +139,10 @@ func (_m *Storage) Reset(ctx context.Context) error {
 func (_m *Storage) Update(ctx context.Context, update *spaces.Space, filter *spaces.Filter) (int, int, error) {
 	ret := _m.Called(ctx, update, filter)
 
+	if len(ret) == 0 {
+		panic("no return value specified for Update")
+	}
+
 	var r0 int
 	var r1 int
 	var r2 error
@@ -146,13 +170,12 @@ func (_m *Storage) Update(ctx context.Context, update *spaces.Space, filter *spa
 	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/spaces/service.go b/pkg/spaces/service.go
index 25d106407450fd111f88690c7de1a60f1670a09c..249221692684110e96c62fce37d70fc650d06945 100644
--- a/pkg/spaces/service.go
+++ b/pkg/spaces/service.go
@@ -2,11 +2,12 @@ package spaces
 
 import (
 	"context"
+	"slices"
 	"time"
 
-	"github.com/avast/retry-go/v4"
-
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"github.com/avast/retry-go/v4"
 )
 
 // @microgen grpc
@@ -27,8 +28,12 @@ type Spaces interface {
 	Create(ctx context.Context, space *Space) (created *Space, err error)
 	Get(ctx context.Context, spaceId string) (space *Space, err error)
 	List(ctx context.Context, orgId string) (spaces []*Space, err error)
+	Find(ctx context.Context, filter *Filter, fo *options.FindOptions) (spaces []*Space, total int, err error)
 	Update(ctx context.Context, space *Space) (err error)
 	UpdateConfig(ctx context.Context, spaceId string, config *Config) (err error)
+
+	// @microgen -
+	SetState(ctx context.Context, spaceID string, state *StateInfo) (err error)
 	Delete(ctx context.Context, spaceId string) (err error)
 
 	// Transfer устанавливает для пространства значение поля RequestedMoveTo. После этого пространство
@@ -62,17 +67,15 @@ var (
 	ErrSpaceNotAvailable = errors.New("space not available")
 )
 
-// IsSpaceAvailable проверяет доступность пространства для работы
-// Если пространство находится в состоянии StateNew или StateReady, то оно доступно для работы
 func IsSpaceAvailable(ctx context.Context, svc Spaces, spaceID string) error {
 	sp, err := svc.Get(ctx, spaceID)
 	if err != nil {
 		return errors.Wrap(err, "fail to get space")
 	}
-	if sp.StateInfo == nil || sp.StateInfo != nil && (sp.StateInfo.State == StateNew || sp.StateInfo.State == StateReady) {
+	if sp.StateInfo == nil || slices.Contains(WriteAllowedStates, sp.StateInfo.State) {
 		return nil
 	}
-	return ErrSpaceNotAvailable
+	return errors.WithContext(ErrSpaceNotAvailable, "state", sp.StateInfo.State)
 }
 
 func IsSpaceReadable(ctx context.Context, svc Spaces, spaceID string) error {
@@ -80,7 +83,7 @@ func IsSpaceReadable(ctx context.Context, svc Spaces, spaceID string) error {
 	if err != nil {
 		return errors.Wrap(err, "fail to get space")
 	}
-	if sp.StateInfo == nil || sp.StateInfo != nil && sp.StateInfo.State == StateNew || sp.StateInfo.State == StateReady || sp.StateInfo.State == StateMigration {
+	if sp.StateInfo == nil || slices.Contains(ReadAllowedStates, sp.StateInfo.State) {
 		return nil
 	}
 	return errors.WithContext(ErrSpaceNotAvailable, "state", sp.StateInfo.State)
diff --git a/pkg/spaces/space.go b/pkg/spaces/space.go
index 83edb3f14706793aed6077adcee856598358b044..baa44124b6830aa8c4f666f0300661952eadbc9d 100644
--- a/pkg/spaces/space.go
+++ b/pkg/spaces/space.go
@@ -10,13 +10,58 @@ const (
 	StateReady             // Пространство готово к использованию
 	StatePreparing         // Пространство готовится в использованию
 	StateMaintenance       // Пространство находится на обслуживании
-	StateMigration         // Пространство в состоянии выполнения миграций
-	StateDeleting          // Пространство удаляется, данные будут удалены
-	StateError             // Произошла ошибка при конфигурации пространства (см. Space.Error)
+
+	// StateMigrationScheduled Для пространства запланирована миграция
+	// При планировании запуска миграции пространства это состояние выступает в роли Lock'а, который не
+	// позволяет запустить несколько миграций одновременно. Логика планировщика миграций сначала пытается
+	// установить для пространства состояние StateMigrationScheduled, что удается только в двух случаях:
+	// пространство находится в состоянии StateReady или же в состояниях StateMigration, StateMigrationScheduled,
+	// но последнее обновление состояния было более 15-ти минут назад
+	StateMigrationScheduled
+	StateMigration // Пространство в состоянии выполнения миграций
+	StateDeleting  // Пространство удаляется, данные будут удалены
+	StateError     // Произошла ошибка при конфигурации пространства (см. Space.Error)
+)
+
+func (s State) String() string {
+	switch s {
+	case StateNew:
+		return "New"
+	case StateReady:
+		return "Ready"
+	case StatePreparing:
+		return "Preparing"
+	case StateMaintenance:
+		return "Maintenance"
+	case StateMigrationScheduled:
+		return "MigrationScheduled"
+	case StateMigration:
+		return "Migration"
+	case StateDeleting:
+		return "Deleting"
+	case StateError:
+		return "Error"
+	default:
+		return "Unknown"
+	}
+}
+
+var (
+	ReadAllowedStates = []State{
+		StateNew,
+		StateReady,
+		StateMigrationScheduled,
+		StateMigration,
+	}
+
+	WriteAllowedStates = []State{
+		StateNew,
+		StateReady,
+	}
 )
 
 type Config struct {
-	Features []string //Deprecated Возможности используемые пространством
+	Features []string // Deprecated Возможности используемые пространством
 }
 
 const StateInfoEmpty = "EMPTY"
@@ -34,12 +79,12 @@ type Space struct {
 }
 
 type StateInfo struct {
-	State     State     `json:"state" bson:"state"`
-	Info      string    `json:"info" bson:"info"`
+	State     State     `json:"state" bson:"state,omitempty"`
+	Info      string    `json:"info" bson:"info,omitempty"`
 	Time      time.Time `json:"time,omitempty" bson:"time,omitempty"`
-	DBVersion uint32    `json:"db_version" bson:"db_version"`
+	DBVersion uint32    `json:"db_version" bson:"db_version,omitempty"`
 }
 
 func (s Space) Clone() *Space {
 	return &s
-}
\ No newline at end of file
+}
diff --git a/pkg/spaces/storage.go b/pkg/spaces/storage.go
index 798dd96bb859e31a6b49ca5a882918519c3e020f..a489c0b56104fdd47c479f1cd89a960f097f4d70 100644
--- a/pkg/spaces/storage.go
+++ b/pkg/spaces/storage.go
@@ -21,4 +21,5 @@ type Filter struct {
 	Name          []string `json:"name,omitempty" bson:"name"`
 	State         []State  `json:"state,omitempty" bson:"state"`
 	TransferToOrg []string `json:"transfer_to_org" bson:"transfer_to_org"`
+	Q             []string `json:"q" bson:"q"`
 }
diff --git a/pkg/spaces/transport/client.go b/pkg/spaces/transport/client.go
index 26900dad00a30d5c685ce507626dd7d93d847c87..52a30a561eb51812831f15b26c1b1665691ee6d3 100644
--- a/pkg/spaces/transport/client.go
+++ b/pkg/spaces/transport/client.go
@@ -5,6 +5,7 @@ package transport
 import (
 	"context"
 
+	"git.perx.ru/perxis/perxis-go/pkg/options"
 	spaces "git.perx.ru/perxis/perxis-go/pkg/spaces"
 )
 
@@ -35,6 +36,15 @@ func (set EndpointsSet) List(arg0 context.Context, arg1 string) (res0 []*spaces.
 	return response.(*ListResponse).Spaces, res1
 }
 
+func (set EndpointsSet) Find(arg0 context.Context, arg1 *spaces.Filter, arg2 *options.FindOptions) (res0 []*spaces.Space, res1 int, res2 error) {
+	request := FindRequest{Filter: arg1, Options: arg2}
+	response, res2 := set.FindEndpoint(arg0, &request)
+	if res2 != nil {
+		return
+	}
+	return response.(*FindResponse).Spaces, response.(*FindResponse).Total, res2
+}
+
 func (set EndpointsSet) Update(arg0 context.Context, arg1 *spaces.Space) (res0 error) {
 	request := UpdateRequest{Space: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
@@ -56,6 +66,10 @@ func (set EndpointsSet) UpdateConfig(arg0 context.Context, arg1 string, arg2 *sp
 	return res0
 }
 
+func (set EndpointsSet) SetState(arg0 context.Context, arg1 string, arg2 *spaces.StateInfo) (res0 error) {
+	return
+}
+
 func (set EndpointsSet) Delete(arg0 context.Context, arg1 string) (res0 error) {
 	request := DeleteRequest{SpaceId: arg1}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
diff --git a/pkg/spaces/transport/endpoints.microgen.go b/pkg/spaces/transport/endpoints.microgen.go
index 335f9ca4f52a6378f41563733e4b6f4f84b77d4c..8ab541ba98b0a4475addcd7e6e7dd4f7c1f2ed2d 100644
--- a/pkg/spaces/transport/endpoints.microgen.go
+++ b/pkg/spaces/transport/endpoints.microgen.go
@@ -9,6 +9,7 @@ type EndpointsSet struct {
 	CreateEndpoint        endpoint.Endpoint
 	GetEndpoint           endpoint.Endpoint
 	ListEndpoint          endpoint.Endpoint
+	FindEndpoint          endpoint.Endpoint
 	UpdateEndpoint        endpoint.Endpoint
 	UpdateConfigEndpoint  endpoint.Endpoint
 	DeleteEndpoint        endpoint.Endpoint
diff --git a/pkg/spaces/transport/exchanges.microgen.go b/pkg/spaces/transport/exchanges.microgen.go
index 414ec9702c29fc0bef9fe84f728d1deaa816667a..5fe6cfbad14530dc543091ea0df0dc960f75a023 100644
--- a/pkg/spaces/transport/exchanges.microgen.go
+++ b/pkg/spaces/transport/exchanges.microgen.go
@@ -2,7 +2,10 @@
 
 package transport
 
-import spaces "git.perx.ru/perxis/perxis-go/pkg/spaces"
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	spaces "git.perx.ru/perxis/perxis-go/pkg/spaces"
+)
 
 type (
 	CreateRequest struct {
@@ -26,6 +29,15 @@ type (
 		Spaces []*spaces.Space `json:"spaces"`
 	}
 
+	FindRequest struct {
+		Filter  *spaces.Filter       `json:"filter"`
+		Options *options.FindOptions `json:"options"`
+	}
+	FindResponse struct {
+		Spaces []*spaces.Space `json:"spaces"`
+		Total  int             `json:"total"`
+	}
+
 	UpdateRequest struct {
 		Space *spaces.Space `json:"space"`
 	}
diff --git a/pkg/spaces/transport/grpc/client.go b/pkg/spaces/transport/grpc/client.go
index 67e3f4019f1b1351d9985403ed39f17912913625..a4c431a7d80e0953a46fb9e7048ef63fa8f31a93 100644
--- a/pkg/spaces/transport/grpc/client.go
+++ b/pkg/spaces/transport/grpc/client.go
@@ -15,6 +15,7 @@ func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.En
 		CreateEndpoint:        grpcerr.ClientMiddleware(c.CreateEndpoint),
 		GetEndpoint:           grpcerr.ClientMiddleware(c.GetEndpoint),
 		ListEndpoint:          grpcerr.ClientMiddleware(c.ListEndpoint),
+		FindEndpoint:          grpcerr.ClientMiddleware(c.FindEndpoint),
 		UpdateEndpoint:        grpcerr.ClientMiddleware(c.UpdateEndpoint),
 		UpdateConfigEndpoint:  grpcerr.ClientMiddleware(c.UpdateConfigEndpoint),
 		DeleteEndpoint:        grpcerr.ClientMiddleware(c.DeleteEndpoint),
diff --git a/pkg/spaces/transport/grpc/client.microgen.go b/pkg/spaces/transport/grpc/client.microgen.go
index 9a92bde894407be7520aa0f27a2112c5f4294cf0..1e7095cd261f03111fafaafc361ca49b7f12cb70 100644
--- a/pkg/spaces/transport/grpc/client.microgen.go
+++ b/pkg/spaces/transport/grpc/client.microgen.go
@@ -50,6 +50,13 @@ func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOpt
 			pb.ListResponse{},
 			opts...,
 		).Endpoint(),
+		FindEndpoint: grpckit.NewClient(
+			conn, addr, "Find",
+			_Encode_Find_Request,
+			_Decode_Find_Response,
+			pb.FindResponse{},
+			opts...,
+		).Endpoint(),
 		ListTransfersEndpoint: grpckit.NewClient(
 			conn, addr, "ListTransfers",
 			_Encode_ListTransfers_Request,
diff --git a/pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go
index efe05ac83e9006e3d8345d25beca40fa0f4187ac..6797cc200801de828c8cefe14c085280188a6a75 100644
--- a/pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go
+++ b/pkg/spaces/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -7,6 +7,7 @@ import (
 	"context"
 	"errors"
 
+	transportgrpc "git.perx.ru/perxis/perxis-go/pkg/items/transport/grpc"
 	transport "git.perx.ru/perxis/perxis-go/pkg/spaces/transport"
 	pb "git.perx.ru/perxis/perxis-go/proto/spaces"
 	empty "google.golang.org/protobuf/types/known/emptypb"
@@ -40,6 +41,22 @@ func _Encode_List_Request(ctx context.Context, request interface{}) (interface{}
 	return &pb.ListRequest{OrgId: req.OrgId}, nil
 }
 
+func _Encode_Find_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil FindRequest")
+	}
+	req := request.(*transport.FindRequest)
+	reqFilter, err := SpaceFilterToProto(req.Filter)
+	if err != nil {
+		return nil, err
+	}
+	reqOptions, err := transportgrpc.PtrServicesFindOptionsToProto(req.Options)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.FindRequest{Filter: reqFilter, Options: reqOptions}, nil
+}
+
 func _Encode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
 	if request == nil {
 		return nil, errors.New("nil UpdateRequest")
@@ -111,6 +128,18 @@ func _Encode_List_Response(ctx context.Context, response interface{}) (interface
 	return &pb.ListResponse{Spaces: respSpaces}, nil
 }
 
+func _Encode_Find_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil FindResponse")
+	}
+	resp := response.(*transport.FindResponse)
+	respSpaces, err := ListPtrSpaceToProto(resp.Spaces)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.FindResponse{Spaces: respSpaces, Total: int32(resp.Total)}, nil
+}
+
 func _Encode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
 	return &empty.Empty{}, nil
 }
@@ -151,6 +180,24 @@ func _Decode_List_Request(ctx context.Context, request interface{}) (interface{}
 	return &transport.ListRequest{OrgId: string(req.OrgId)}, nil
 }
 
+func _Decode_Find_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	if request == nil {
+		return nil, errors.New("nil FindRequest")
+	}
+	req := request.(*pb.FindRequest)
+
+	opts, err := transportgrpc.ProtoToPtrServicesFindOptions(req.Options)
+	if err != nil {
+		return nil, err
+	}
+	filter, err := ProtoToPtrSpaceFilter(req.Filter)
+	if err != nil {
+		return nil, err
+	}
+
+	return &transport.FindRequest{Filter: filter, Options: opts}, nil
+}
+
 func _Decode_Update_Request(ctx context.Context, request interface{}) (interface{}, error) {
 	if request == nil {
 		return nil, errors.New("nil UpdateRequest")
@@ -222,6 +269,18 @@ func _Decode_List_Response(ctx context.Context, response interface{}) (interface
 	return &transport.ListResponse{Spaces: respSpaces}, nil
 }
 
+func _Decode_Find_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil FindResponse")
+	}
+	resp := response.(*pb.FindResponse)
+	respSpaces, err := ProtoToListPtrSpace(resp.Spaces)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.FindResponse{Spaces: respSpaces, Total: int(resp.Total)}, nil
+}
+
 func _Decode_Update_Response(ctx context.Context, response interface{}) (interface{}, error) {
 	return &empty.Empty{}, nil
 }
diff --git a/pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go b/pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go
index 70081af310c72b62f2cc1a7ac0bc32fed007ac60..d7231bdba357885b1506b3f5d2301149feb8cd5a 100644
--- a/pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go
+++ b/pkg/spaces/transport/grpc/protobuf_type_converters.microgen.go
@@ -108,3 +108,41 @@ func ProtoToListPtrSpace(protoSpaces []*pb.Space) ([]*service.Space, error) {
 	}
 	return spaces, nil
 }
+
+func ProtoToPtrSpaceFilter(protoSpaceFilter *pb.Filter) (*service.Filter, error) {
+	if protoSpaceFilter == nil {
+		return nil, nil
+	}
+	var srvState []service.State
+	if len(protoSpaceFilter.State) != 0 {
+		for _, state := range protoSpaceFilter.State {
+			srvState = append(srvState, service.State(state))
+		}
+	}
+	return &service.Filter{
+		ID:            protoSpaceFilter.Id,
+		OrgID:         protoSpaceFilter.OrgId,
+		Name:          protoSpaceFilter.Name,
+		State:         srvState,
+		TransferToOrg: protoSpaceFilter.TransferToOrg,
+	}, nil
+}
+
+func SpaceFilterToProto(spaceFilter *service.Filter) (*pb.Filter, error) {
+	if spaceFilter == nil {
+		return nil, nil
+	}
+	var pbState []pb.State
+	if len(spaceFilter.State) != 0 {
+		for _, state := range spaceFilter.State {
+			pbState = append(pbState, pb.State(state))
+		}
+	}
+	return &pb.Filter{
+		Id:            spaceFilter.ID,
+		OrgId:         spaceFilter.OrgID,
+		Name:          spaceFilter.Name,
+		State:         pbState,
+		TransferToOrg: spaceFilter.TransferToOrg,
+	}, nil
+}
diff --git a/pkg/spaces/transport/grpc/server.go b/pkg/spaces/transport/grpc/server.go
index 8819d2a3637ae8bc1b3820b1e8cb110580a9b37f..01574994044d527046ad6f33ac631c37bbb03e1f 100644
--- a/pkg/spaces/transport/grpc/server.go
+++ b/pkg/spaces/transport/grpc/server.go
@@ -14,6 +14,7 @@ func NewServer(svc spaces.Spaces, opts ...grpckit.ServerOption) pb.SpacesServer
 		CreateEndpoint:        grpcerr.ServerMiddleware(eps.CreateEndpoint),
 		GetEndpoint:           grpcerr.ServerMiddleware(eps.GetEndpoint),
 		ListEndpoint:          grpcerr.ServerMiddleware(eps.ListEndpoint),
+		FindEndpoint:          grpcerr.ServerMiddleware(eps.FindEndpoint),
 		UpdateEndpoint:        grpcerr.ServerMiddleware(eps.UpdateEndpoint),
 		UpdateConfigEndpoint:  grpcerr.ServerMiddleware(eps.UpdateConfigEndpoint),
 		DeleteEndpoint:        grpcerr.ServerMiddleware(eps.DeleteEndpoint),
diff --git a/pkg/spaces/transport/grpc/server.microgen.go b/pkg/spaces/transport/grpc/server.microgen.go
index bf33dbbeea79339df43617ab97f460ca27d927b4..2a39455033e628b9ac7766b6057575793e2f8f6c 100644
--- a/pkg/spaces/transport/grpc/server.microgen.go
+++ b/pkg/spaces/transport/grpc/server.microgen.go
@@ -15,6 +15,7 @@ type spacesServer struct {
 	create        grpc.Handler
 	get           grpc.Handler
 	list          grpc.Handler
+	find          grpc.Handler
 	update        grpc.Handler
 	updateConfig  grpc.Handler
 	delete        grpc.Handler
@@ -58,6 +59,12 @@ func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption)
 			_Encode_List_Response,
 			opts...,
 		),
+		find: grpc.NewServer(
+			endpoints.FindEndpoint,
+			_Decode_Find_Request,
+			_Encode_Find_Response,
+			opts...,
+		),
 		listTransfers: grpc.NewServer(
 			endpoints.ListTransfersEndpoint,
 			_Decode_ListTransfers_Request,
@@ -115,6 +122,14 @@ func (S *spacesServer) List(ctx context.Context, req *pb.ListRequest) (*pb.ListR
 	return resp.(*pb.ListResponse), nil
 }
 
+func (S *spacesServer) Find(ctx context.Context, req *pb.FindRequest) (*pb.FindResponse, error) {
+	_, resp, err := S.find.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.FindResponse), nil
+}
+
 func (S *spacesServer) Update(ctx context.Context, req *pb.UpdateRequest) (*empty.Empty, error) {
 	_, resp, err := S.update.ServeGRPC(ctx, req)
 	if err != nil {
diff --git a/pkg/spaces/transport/server.microgen.go b/pkg/spaces/transport/server.microgen.go
index 60a07de0bf59846a2695f3c7625b3565044e823b..ece65a8fefe877cb031a9dc7854c141d3545d8fe 100644
--- a/pkg/spaces/transport/server.microgen.go
+++ b/pkg/spaces/transport/server.microgen.go
@@ -16,6 +16,7 @@ func Endpoints(svc spaces.Spaces) EndpointsSet {
 		DeleteEndpoint:        DeleteEndpoint(svc),
 		GetEndpoint:           GetEndpoint(svc),
 		ListEndpoint:          ListEndpoint(svc),
+		FindEndpoint:          FindEndpoint(svc),
 		ListTransfersEndpoint: ListTransfersEndpoint(svc),
 		MoveEndpoint:          MoveEndpoint(svc),
 		TransferEndpoint:      TransferEndpoint(svc),
@@ -48,6 +49,14 @@ func ListEndpoint(svc spaces.Spaces) endpoint.Endpoint {
 	}
 }
 
+func FindEndpoint(svc spaces.Spaces) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		req := request.(*FindRequest)
+		res0, res1, res2 := svc.Find(arg0, req.Filter, req.Options)
+		return &FindResponse{Spaces: res0, Total: res1}, res2
+	}
+}
+
 func UpdateEndpoint(svc spaces.Spaces) endpoint.Endpoint {
 	return func(arg0 context.Context, request interface{}) (interface{}, error) {
 		req := request.(*UpdateRequest)
diff --git a/pkg/users/events.go b/pkg/users/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..c262c017afe6146e1b7c91e031d5575c381c3cc6
--- /dev/null
+++ b/pkg/users/events.go
@@ -0,0 +1,7 @@
+package users
+
+const (
+	EventCreate = "users.create"
+	EventUpdate = "users.update"
+	EventDelete = "users.delete"
+)
diff --git a/pkg/users/middleware/caching_middleware.go b/pkg/users/middleware/caching_middleware.go
index 8397bfd3a5c2a02c9ff52c42f9d1e23d431aed8e..825da6a28caabeee0f71d9030b60c8152e884570 100644
--- a/pkg/users/middleware/caching_middleware.go
+++ b/pkg/users/middleware/caching_middleware.go
@@ -30,16 +30,17 @@ func (m cachingMiddleware) Get(ctx context.Context, userId string) (user *servic
 
 	value, e := m.cache.Get(userId)
 	if e == nil {
-		return value.(*service.User), err
+		return value.(*service.User).Clone(), nil
 	}
 	user, err = m.next.Get(ctx, userId)
 	if err == nil {
-		m.cache.Set(user.ID, user)
+		_ = m.cache.Set(user.ID, user)
 		for _, i := range user.Identities {
-			m.cache.Set(i, user)
+			_ = m.cache.Set(i, user)
 		}
+		return user.Clone(), nil
 	}
-	return user, err
+	return nil, err
 }
 
 func (m cachingMiddleware) Find(ctx context.Context, filter *service.Filter, options *services.FindOptions) (users []*service.User, total int, err error) {
@@ -52,9 +53,9 @@ func (m cachingMiddleware) Update(ctx context.Context, update *service.User) (er
 	value, e := m.cache.Get(update.ID)
 	if err == nil && e == nil {
 		usr := value.(*service.User)
-		m.cache.Remove(usr.ID)
+		_ = m.cache.Remove(usr.ID)
 		for _, i := range usr.Identities {
-			m.cache.Remove(i)
+			_ = m.cache.Remove(i)
 		}
 	}
 	return err
@@ -66,9 +67,9 @@ func (m cachingMiddleware) Delete(ctx context.Context, userId string) (err error
 	value, e := m.cache.Get(userId)
 	if err == nil && e == nil {
 		usr := value.(*service.User)
-		m.cache.Remove(usr.ID)
+		_ = m.cache.Remove(usr.ID)
 		for _, i := range usr.Identities {
-			m.cache.Remove(i)
+			_ = m.cache.Remove(i)
 		}
 	}
 	return err
@@ -78,14 +79,15 @@ func (m cachingMiddleware) GetByIdentity(ctx context.Context, identity string) (
 
 	value, e := m.cache.Get(identity)
 	if e == nil {
-		return value.(*service.User), err
+		return value.(*service.User).Clone(), nil
 	}
 	user, err = m.next.GetByIdentity(ctx, identity)
 	if err == nil {
-		m.cache.Set(user.ID, user)
+		_ = m.cache.Set(user.ID, user)
 		for _, i := range user.Identities {
-			m.cache.Set(i, user)
+			_ = m.cache.Set(i, user)
 		}
+		return user.Clone(), nil
 	}
-	return user, err
+	return nil, err
 }
diff --git a/pkg/users/middleware/caching_middleware_test.go b/pkg/users/middleware/caching_middleware_test.go
index edc9176baac6656a1d70995b0881055b83642293..c5755407c6ffbc163f86ed548ad4b50c5d8bfc94 100644
--- a/pkg/users/middleware/caching_middleware_test.go
+++ b/pkg/users/middleware/caching_middleware_test.go
@@ -38,11 +38,13 @@ func TestUsersCache(t *testing.T) {
 
 		v2, err := svc.Get(ctx, userID)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.GetByIdentity(ctx, identity)
 		require.NoError(t, err)
-		assert.Same(t, v2, v3, "Ожидается получение объекта из кэша при запросе по Identity.")
+		assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша при запросе по Identity.")
+		assert.NotSame(t, v2, v3)
 
 		usrs.AssertExpectations(t)
 	})
@@ -60,11 +62,13 @@ func TestUsersCache(t *testing.T) {
 
 		v2, err := svc.GetByIdentity(ctx, identity)
 		require.NoError(t, err)
-		assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+		assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+		assert.NotSame(t, v1, v2)
 
 		v3, err := svc.Get(ctx, userID)
 		require.NoError(t, err)
-		assert.Same(t, v2, v3, "Ожидается получение объекта из кэша при запросе по userID.")
+		assert.Equal(t, v2, v3, "Ожидается получение объекта из кэша при запросе по userID.")
+		assert.NotSame(t, v2, v3)
 
 		usrs.AssertExpectations(t)
 	})
@@ -84,7 +88,8 @@ func TestUsersCache(t *testing.T) {
 
 			v2, err := svc.GetByIdentity(ctx, identity)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			err = svc.Update(ctx, &users.User{ID: userID, Name: "New User", Identities: []string{identity}})
 			require.NoError(t, err)
@@ -93,12 +98,13 @@ func TestUsersCache(t *testing.T) {
 
 			v3, err := svc.GetByIdentity(ctx, identity)
 			require.NoError(t, err)
-			assert.NotSame(t, v3, v2, "Ожидается удаление объекта из кеша после обновления и получение его заново из сервиса.")
+			assert.NotEqual(t, v3, v2, "Ожидается удаление объекта из кеша после обновления и получение его заново из сервиса.")
 
 			v4, err := svc.Get(ctx, userID)
 			require.NoError(t, err)
-			assert.NotSame(t, v4, v2)
-			assert.Same(t, v4, v3, "Ожидается получение нового обьекта из кеша.")
+			assert.NotEqual(t, v4, v2)
+			assert.Equal(t, v4, v3, "Ожидается получение нового обьекта из кеша.")
+			assert.NotSame(t, v4, v3)
 
 			usrs.AssertExpectations(t)
 		})
@@ -117,7 +123,8 @@ func TestUsersCache(t *testing.T) {
 
 			v2, err := svc.GetByIdentity(ctx, identity)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			err = svc.Delete(ctx, userID)
 			require.NoError(t, err)
@@ -149,7 +156,8 @@ func TestUsersCache(t *testing.T) {
 
 			v2, err := svc.Get(ctx, userID)
 			require.NoError(t, err)
-			assert.Same(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.Equal(t, v1, v2, "Ожидается получение объекта из кэша.")
+			assert.NotSame(t, v1, v2)
 
 			time.Sleep(2 * ttl)
 
@@ -158,6 +166,8 @@ func TestUsersCache(t *testing.T) {
 			v3, err := svc.Get(ctx, userID)
 			require.NoError(t, err)
 			assert.NotSame(t, v2, v3, "Ожидается получение объекта из кэша при запросе по Identity.")
+			assert.Equal(t, v2, v3)
+			assert.NotSame(t, v2, v3)
 
 			usrs.AssertExpectations(t)
 		})
diff --git a/pkg/users/middleware/logging_middleware.go b/pkg/users/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..3350d36d87b5715c043455428cc6e4aa0c0f981d
--- /dev/null
+++ b/pkg/users/middleware/logging_middleware.go
@@ -0,0 +1,121 @@
+package middleware
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/id"
+	"git.perx.ru/perxis/perxis-go/pkg/options"
+	"git.perx.ru/perxis/perxis-go/pkg/users"
+	logzap "git.perx.ru/perxis/perxis-go/zap"
+	"go.uber.org/zap"
+)
+
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   users.Users
+}
+
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next users.Users) users.Users {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger.With(logzap.Component("Users")),
+		}
+	}
+}
+
+func (m *loggingMiddleware) Create(ctx context.Context, create *users.User) (user *users.User, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(users.EventCreate),
+	)
+
+	user, err = m.next.Create(ctx, create)
+	if err != nil {
+		logger.Error("Failed to create", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog), logzap.Object(user))
+		return
+	}
+
+	logger.Info("User created", logzap.Channels(logzap.Userlog), logzap.Object(user))
+
+	return user, err
+}
+
+func (m *loggingMiddleware) Get(ctx context.Context, userId string) (user *users.User, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewUserId(userId)),
+	)
+
+	user, err = m.next.Get(ctx, userId)
+	if err != nil {
+		logger.Error("Failed to get", zap.Error(err))
+		return
+	}
+
+	return user, err
+}
+
+func (m *loggingMiddleware) Find(ctx context.Context, filter *users.Filter, options *options.FindOptions) (found []*users.User, total int, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+	)
+
+	found, total, err = m.next.Find(ctx, filter, options)
+	if err != nil {
+		logger.Error("Failed to find", zap.Error(err))
+		return
+	}
+
+	return found, total, err
+}
+
+func (m *loggingMiddleware) Update(ctx context.Context, update *users.User) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(users.EventUpdate),
+		logzap.Object(update),
+	)
+
+	err = m.next.Update(ctx, update)
+	if err != nil {
+		logger.Error("Failed to update", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("User updated", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) Delete(ctx context.Context, userId string) (err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Event(users.EventDelete),
+		logzap.Object(id.NewUserId(userId)),
+	)
+
+	err = m.next.Delete(ctx, userId)
+	if err != nil {
+		logger.Error("Failed to delete", zap.Error(err), logzap.Channels(logzap.Userlog, logzap.Syslog))
+		return
+	}
+
+	logger.Info("User deleted", logzap.Channels(logzap.Userlog))
+
+	return err
+}
+
+func (m *loggingMiddleware) GetByIdentity(ctx context.Context, identity string) (user *users.User, err error) {
+	logger := m.logger.With(
+		logzap.Caller(ctx),
+		logzap.Object(id.NewUserId(identity)),
+	)
+
+	user, err = m.next.GetByIdentity(ctx, identity)
+	if err != nil {
+		logger.Error("Failed to get by identity", zap.Error(err))
+		return
+	}
+	return user, err
+}
diff --git a/pkg/users/middleware/middleware.go b/pkg/users/middleware/middleware.go
index d94190827de3028c96000df15bf06c719e30cf92..ed64ceeddae0ab089c1746356ece1d85a0c69e83 100644
--- a/pkg/users/middleware/middleware.go
+++ b/pkg/users/middleware/middleware.go
@@ -21,7 +21,7 @@ func WithLog(s users.Users, logger *zap.Logger, log_access bool) users.Users {
 	if log_access {
 		s = AccessLoggingMiddleware(logger)(s)
 	}
-	s = ErrorLoggingMiddleware(logger)(s)
+	s = LoggingMiddleware(logger)(s)
 
 	s = RecoveringMiddleware(logger)(s)
 	return s
diff --git a/pkg/users/transport/client.microgen.go b/pkg/users/transport/client.go
similarity index 66%
rename from pkg/users/transport/client.microgen.go
rename to pkg/users/transport/client.go
index 74ca261a3bc5ec1cf99c655c82ec5b0345489ce3..01537b1123040311fcd9fb036c8765066d2f65b1 100644
--- a/pkg/users/transport/client.microgen.go
+++ b/pkg/users/transport/client.go
@@ -4,21 +4,15 @@ package transport
 
 import (
 	"context"
-	"errors"
 
 	options "git.perx.ru/perxis/perxis-go/pkg/options"
 	users "git.perx.ru/perxis/perxis-go/pkg/users"
-	codes "google.golang.org/grpc/codes"
-	status "google.golang.org/grpc/status"
 )
 
 func (set EndpointsSet) Create(arg0 context.Context, arg1 *users.User) (res0 *users.User, res1 error) {
 	request := CreateRequest{Create: arg1}
 	response, res1 := set.CreateEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*CreateResponse).User, res1
@@ -28,9 +22,6 @@ func (set EndpointsSet) Get(arg0 context.Context, arg1 string) (res0 *users.User
 	request := GetRequest{UserId: arg1}
 	response, res1 := set.GetEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetResponse).User, res1
@@ -43,9 +34,6 @@ func (set EndpointsSet) Find(arg0 context.Context, arg1 *users.Filter, arg2 *opt
 	}
 	response, res2 := set.FindEndpoint(arg0, &request)
 	if res2 != nil {
-		if e, ok := status.FromError(res2); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res2 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*FindResponse).Users, response.(*FindResponse).Total, res2
@@ -55,9 +43,6 @@ func (set EndpointsSet) Update(arg0 context.Context, arg1 *users.User) (res0 err
 	request := UpdateRequest{Update: arg1}
 	_, res0 = set.UpdateEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -67,9 +52,6 @@ func (set EndpointsSet) Delete(arg0 context.Context, arg1 string) (res0 error) {
 	request := DeleteRequest{UserId: arg1}
 	_, res0 = set.DeleteEndpoint(arg0, &request)
 	if res0 != nil {
-		if e, ok := status.FromError(res0); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res0 = errors.New(e.Message())
-		}
 		return
 	}
 	return res0
@@ -79,9 +61,6 @@ func (set EndpointsSet) GetByIdentity(arg0 context.Context, arg1 string) (res0 *
 	request := GetByIdentityRequest{Identity: arg1}
 	response, res1 := set.GetByIdentityEndpoint(arg0, &request)
 	if res1 != nil {
-		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
-			res1 = errors.New(e.Message())
-		}
 		return
 	}
 	return response.(*GetByIdentityResponse).User, res1
diff --git a/pkg/users/transport/grpc/client.go b/pkg/users/transport/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..7364d5fce73a58580ed59bd71539bab3a1e68668
--- /dev/null
+++ b/pkg/users/transport/grpc/client.go
@@ -0,0 +1,22 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	transport "git.perx.ru/perxis/perxis-go/pkg/users/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewClient(conn *grpc.ClientConn, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	c := NewGRPCClient(conn, "", opts...)
+	return transport.EndpointsSet{
+		CreateEndpoint:        grpcerr.ClientMiddleware(c.CreateEndpoint),
+		DeleteEndpoint:        grpcerr.ClientMiddleware(c.DeleteEndpoint),
+		FindEndpoint:          grpcerr.ClientMiddleware(c.FindEndpoint),
+		GetByIdentityEndpoint: grpcerr.ClientMiddleware(c.GetByIdentityEndpoint),
+		GetEndpoint:           grpcerr.ClientMiddleware(c.GetEndpoint),
+		UpdateEndpoint:        grpcerr.ClientMiddleware(c.UpdateEndpoint),
+	}
+}
diff --git a/pkg/users/transport/grpc/server.go b/pkg/users/transport/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..07211008e3d631e1c23a658a31fa67f8f983a058
--- /dev/null
+++ b/pkg/users/transport/grpc/server.go
@@ -0,0 +1,22 @@
+package transportgrpc
+
+import (
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/users"
+	"git.perx.ru/perxis/perxis-go/pkg/users/transport"
+	pb "git.perx.ru/perxis/perxis-go/proto/users"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+)
+
+func NewServer(svc users.Users, opts ...grpckit.ServerOption) pb.UsersServer {
+	eps := transport.Endpoints(svc)
+	eps = transport.EndpointsSet{
+		CreateEndpoint:        grpcerr.ServerMiddleware(eps.CreateEndpoint),
+		DeleteEndpoint:        grpcerr.ServerMiddleware(eps.DeleteEndpoint),
+		FindEndpoint:          grpcerr.ServerMiddleware(eps.FindEndpoint),
+		GetByIdentityEndpoint: grpcerr.ServerMiddleware(eps.GetByIdentityEndpoint),
+		GetEndpoint:           grpcerr.ServerMiddleware(eps.GetEndpoint),
+		UpdateEndpoint:        grpcerr.ServerMiddleware(eps.UpdateEndpoint),
+	}
+	return NewGRPCServer(&eps, opts...)
+}
diff --git a/proto/clients/clients.pb.go b/proto/clients/clients.pb.go
index a492dbad2bd2bfa0dd320e869f28f9b1ea5146fd..250cdd86d74dd1bf53f58b9c56109e270b25443f 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.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: clients/clients.proto
 
 package clients
@@ -1026,7 +1026,7 @@ func file_clients_clients_proto_rawDescGZIP() []byte {
 }
 
 var file_clients_clients_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
-var file_clients_clients_proto_goTypes = []interface{}{
+var file_clients_clients_proto_goTypes = []any{
 	(*Client)(nil),         // 0: content.clients.Client
 	(*CreateRequest)(nil),  // 1: content.clients.CreateRequest
 	(*CreateResponse)(nil), // 2: content.clients.CreateResponse
@@ -1081,7 +1081,7 @@ func file_clients_clients_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_clients_clients_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Client); i {
 			case 0:
 				return &v.state
@@ -1093,7 +1093,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -1105,7 +1105,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -1117,7 +1117,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -1129,7 +1129,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -1141,7 +1141,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*GetByRequest); i {
 			case 0:
 				return &v.state
@@ -1153,7 +1153,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*GetByResponse); i {
 			case 0:
 				return &v.state
@@ -1165,7 +1165,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -1177,7 +1177,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest); i {
 			case 0:
 				return &v.state
@@ -1189,7 +1189,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*ListResponse); i {
 			case 0:
 				return &v.state
@@ -1201,7 +1201,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -1213,7 +1213,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*EnableRequest); i {
 			case 0:
 				return &v.state
@@ -1225,7 +1225,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*Client_OAuth); i {
 			case 0:
 				return &v.state
@@ -1237,7 +1237,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*Client_APIKey); i {
 			case 0:
 				return &v.state
@@ -1249,7 +1249,7 @@ func file_clients_clients_proto_init() {
 				return nil
 			}
 		}
-		file_clients_clients_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_clients_clients_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*Client_TLS); i {
 			case 0:
 				return &v.state
@@ -1262,7 +1262,7 @@ func file_clients_clients_proto_init() {
 			}
 		}
 	}
-	file_clients_clients_proto_msgTypes[0].OneofWrappers = []interface{}{}
+	file_clients_clients_proto_msgTypes[0].OneofWrappers = []any{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
diff --git a/proto/clients/clients_grpc.pb.go b/proto/clients/clients_grpc.pb.go
index f8fdf0cda264fa6e99aa20ecf495050dc1231fc7..b2e006de4d8bed8870a7094a63fafde21cff5e5c 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.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: clients/clients.proto
 
 package clients
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Clients_Create_FullMethodName = "/content.clients.Clients/Create"
@@ -58,8 +58,9 @@ func NewClientsClient(cc grpc.ClientConnInterface) ClientsClient {
 }
 
 func (c *clientsClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Clients_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -67,8 +68,9 @@ func (c *clientsClient) Create(ctx context.Context, in *CreateRequest, opts ...g
 }
 
 func (c *clientsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Clients_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -76,8 +78,9 @@ func (c *clientsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Ca
 }
 
 func (c *clientsClient) GetBy(ctx context.Context, in *GetByRequest, opts ...grpc.CallOption) (*GetByResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetByResponse)
-	err := c.cc.Invoke(ctx, Clients_GetBy_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_GetBy_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -85,8 +88,9 @@ func (c *clientsClient) GetBy(ctx context.Context, in *GetByRequest, opts ...grp
 }
 
 func (c *clientsClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Clients_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -94,8 +98,9 @@ func (c *clientsClient) Update(ctx context.Context, in *UpdateRequest, opts ...g
 }
 
 func (c *clientsClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListResponse)
-	err := c.cc.Invoke(ctx, Clients_List_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_List_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -103,8 +108,9 @@ func (c *clientsClient) List(ctx context.Context, in *ListRequest, opts ...grpc.
 }
 
 func (c *clientsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Clients_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -112,8 +118,9 @@ func (c *clientsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...g
 }
 
 func (c *clientsClient) Enable(ctx context.Context, in *EnableRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Clients_Enable_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Clients_Enable_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -122,7 +129,7 @@ func (c *clientsClient) Enable(ctx context.Context, in *EnableRequest, opts ...g
 
 // ClientsServer is the server API for Clients service.
 // All implementations must embed UnimplementedClientsServer
-// for forward compatibility
+// for forward compatibility.
 type ClientsServer interface {
 	// Create - создает клиента (приложение) для работы с API
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
@@ -141,9 +148,12 @@ type ClientsServer interface {
 	mustEmbedUnimplementedClientsServer()
 }
 
-// UnimplementedClientsServer must be embedded to have forward compatible implementations.
-type UnimplementedClientsServer struct {
-}
+// UnimplementedClientsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedClientsServer struct{}
 
 func (UnimplementedClientsServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -167,6 +177,7 @@ func (UnimplementedClientsServer) Enable(context.Context, *EnableRequest) (*empt
 	return nil, status.Errorf(codes.Unimplemented, "method Enable not implemented")
 }
 func (UnimplementedClientsServer) mustEmbedUnimplementedClientsServer() {}
+func (UnimplementedClientsServer) testEmbeddedByValue()                 {}
 
 // UnsafeClientsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to ClientsServer will
@@ -176,6 +187,13 @@ type UnsafeClientsServer interface {
 }
 
 func RegisterClientsServer(s grpc.ServiceRegistrar, srv ClientsServer) {
+	// If the following call pancis, it indicates UnimplementedClientsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Clients_ServiceDesc, srv)
 }
 
diff --git a/proto/collaborators/collaborators.pb.go b/proto/collaborators/collaborators.pb.go
index e07247b5dce02708390f23a576462dcb965b772d..bb3aad637b6f28510d386714eac5698a6451d484 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: collaborators/collaborators.proto
 
 package collaborators
@@ -521,7 +521,7 @@ func file_collaborators_collaborators_proto_rawDescGZIP() []byte {
 }
 
 var file_collaborators_collaborators_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
-var file_collaborators_collaborators_proto_goTypes = []interface{}{
+var file_collaborators_collaborators_proto_goTypes = []any{
 	(*SetRequest)(nil),                // 0: content.collaborators.SetRequest
 	(*GetRequest)(nil),                // 1: content.collaborators.GetRequest
 	(*GetResponse)(nil),               // 2: content.collaborators.GetResponse
@@ -559,7 +559,7 @@ func file_collaborators_collaborators_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_collaborators_collaborators_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*SetRequest); i {
 			case 0:
 				return &v.state
@@ -571,7 +571,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -583,7 +583,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -595,7 +595,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*RemoveRequest); i {
 			case 0:
 				return &v.state
@@ -607,7 +607,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*ListCollaboratorsRequest); i {
 			case 0:
 				return &v.state
@@ -619,7 +619,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*ListCollaboratorsResponse); i {
 			case 0:
 				return &v.state
@@ -631,7 +631,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*ListSpacesRequest); i {
 			case 0:
 				return &v.state
@@ -643,7 +643,7 @@ func file_collaborators_collaborators_proto_init() {
 				return nil
 			}
 		}
-		file_collaborators_collaborators_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_collaborators_collaborators_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*ListSpacesResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/collaborators/collaborators_grpc.pb.go b/proto/collaborators/collaborators_grpc.pb.go
index 47ff522c1b57c72665fbca4741f698e6bb97b80f..85262442d0f9389cbb258cca420b8eb757b5df1f 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: collaborators/collaborators.proto
 
 package collaborators
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Collaborators_Set_FullMethodName               = "/content.collaborators.Collaborators/Set"
@@ -47,8 +47,9 @@ func NewCollaboratorsClient(cc grpc.ClientConnInterface) CollaboratorsClient {
 }
 
 func (c *collaboratorsClient) Set(ctx context.Context, in *SetRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Collaborators_Set_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collaborators_Set_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -56,8 +57,9 @@ func (c *collaboratorsClient) Set(ctx context.Context, in *SetRequest, opts ...g
 }
 
 func (c *collaboratorsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Collaborators_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collaborators_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -65,8 +67,9 @@ func (c *collaboratorsClient) Get(ctx context.Context, in *GetRequest, opts ...g
 }
 
 func (c *collaboratorsClient) Remove(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Collaborators_Remove_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collaborators_Remove_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -74,8 +77,9 @@ func (c *collaboratorsClient) Remove(ctx context.Context, in *RemoveRequest, opt
 }
 
 func (c *collaboratorsClient) ListCollaborators(ctx context.Context, in *ListCollaboratorsRequest, opts ...grpc.CallOption) (*ListCollaboratorsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListCollaboratorsResponse)
-	err := c.cc.Invoke(ctx, Collaborators_ListCollaborators_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collaborators_ListCollaborators_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -83,8 +87,9 @@ func (c *collaboratorsClient) ListCollaborators(ctx context.Context, in *ListCol
 }
 
 func (c *collaboratorsClient) ListSpaces(ctx context.Context, in *ListSpacesRequest, opts ...grpc.CallOption) (*ListSpacesResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListSpacesResponse)
-	err := c.cc.Invoke(ctx, Collaborators_ListSpaces_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collaborators_ListSpaces_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -93,7 +98,7 @@ func (c *collaboratorsClient) ListSpaces(ctx context.Context, in *ListSpacesRequ
 
 // CollaboratorsServer is the server API for Collaborators service.
 // All implementations must embed UnimplementedCollaboratorsServer
-// for forward compatibility
+// for forward compatibility.
 type CollaboratorsServer interface {
 	Set(context.Context, *SetRequest) (*emptypb.Empty, error)
 	Get(context.Context, *GetRequest) (*GetResponse, error)
@@ -103,9 +108,12 @@ type CollaboratorsServer interface {
 	mustEmbedUnimplementedCollaboratorsServer()
 }
 
-// UnimplementedCollaboratorsServer must be embedded to have forward compatible implementations.
-type UnimplementedCollaboratorsServer struct {
-}
+// UnimplementedCollaboratorsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedCollaboratorsServer struct{}
 
 func (UnimplementedCollaboratorsServer) Set(context.Context, *SetRequest) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Set not implemented")
@@ -123,6 +131,7 @@ func (UnimplementedCollaboratorsServer) ListSpaces(context.Context, *ListSpacesR
 	return nil, status.Errorf(codes.Unimplemented, "method ListSpaces not implemented")
 }
 func (UnimplementedCollaboratorsServer) mustEmbedUnimplementedCollaboratorsServer() {}
+func (UnimplementedCollaboratorsServer) testEmbeddedByValue()                       {}
 
 // UnsafeCollaboratorsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to CollaboratorsServer will
@@ -132,6 +141,13 @@ type UnsafeCollaboratorsServer interface {
 }
 
 func RegisterCollaboratorsServer(s grpc.ServiceRegistrar, srv CollaboratorsServer) {
+	// If the following call pancis, it indicates UnimplementedCollaboratorsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Collaborators_ServiceDesc, srv)
 }
 
diff --git a/proto/collections/collections.pb.go b/proto/collections/collections.pb.go
index 164a8644d7fe74966f55b22880c97cfe05587d8b..01d4dc128c49fae67c535821092e9b60f497add1 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.25.1
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: collections/collections.proto
 
 package collections
@@ -10,6 +10,7 @@ import (
 	common "git.perx.ru/perxis/perxis-go/proto/common"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	durationpb "google.golang.org/protobuf/types/known/durationpb"
 	emptypb "google.golang.org/protobuf/types/known/emptypb"
 	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
 	reflect "reflect"
@@ -188,6 +189,19 @@ type Collection struct {
 	StateInfo *Collection_StateInfo `protobuf:"bytes,10,opt,name=state_info,json=stateInfo,proto3" json:"state_info,omitempty"`
 	Tags      []string              `protobuf:"bytes,16,rep,name=tags,proto3" json:"tags,omitempty"`
 	Access    *Access               `protobuf:"bytes,20,opt,name=access,proto3" json:"access,omitempty"` // Возможные действия с коллекцией на основе контекста запроса
+	// Коллекция без истории изменений (ревизии)
+	// История изменений записей в коллекции не будет сохраняться и вернуться к предыдущим версиям будет нельзя
+	NoRevisions  bool                 `protobuf:"varint,30,opt,name=no_revisions,json=noRevisions,proto3" json:"no_revisions,omitempty"`
+	MaxRevisions uint32               `protobuf:"varint,31,opt,name=max_revisions,json=maxRevisions,proto3" json:"max_revisions,omitempty"` // старые ревизии сверх указанного количества будут автоматически удаляться. 0, пусто - без ограничений
+	RevisionTtl  *durationpb.Duration `protobuf:"bytes,32,opt,name=revision_ttl,json=revisionTtl,proto3" json:"revision_ttl,omitempty"`     // ревизии старше указанного времени хранения будут автоматически удалятся. 0, пусто - без ограничений
+	// Коллекция без архива
+	// Включение опции приведет к удалению всех записей в архиве, а функция архивирования станет недоступна
+	NoArchive bool `protobuf:"varint,35,opt,name=no_archive,json=noArchive,proto3" json:"no_archive,omitempty"`
+	// Коллекция без публикации
+	// Все записи коллекции считаются опубликованными, функции публикации и снятия с публикации недоступны.
+	// При включении параметра коллекции "без публикации" все записи, независимо от статуса, будут считаться опубликованными.
+	// При отключении параметра "без публикации" статусы публикации будут восстановлены.
+	NoPublish bool `protobuf:"varint,40,opt,name=no_publish,json=noPublish,proto3" json:"no_publish,omitempty"`
 }
 
 func (x *Collection) Reset() {
@@ -313,6 +327,41 @@ func (x *Collection) GetAccess() *Access {
 	return nil
 }
 
+func (x *Collection) GetNoRevisions() bool {
+	if x != nil {
+		return x.NoRevisions
+	}
+	return false
+}
+
+func (x *Collection) GetMaxRevisions() uint32 {
+	if x != nil {
+		return x.MaxRevisions
+	}
+	return 0
+}
+
+func (x *Collection) GetRevisionTtl() *durationpb.Duration {
+	if x != nil {
+		return x.RevisionTtl
+	}
+	return nil
+}
+
+func (x *Collection) GetNoArchive() bool {
+	if x != nil {
+		return x.NoArchive
+	}
+	return false
+}
+
+func (x *Collection) GetNoPublish() bool {
+	if x != nil {
+		return x.NoPublish
+	}
+	return false
+}
+
 type CreateRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1088,7 +1137,9 @@ var file_collections_collections_proto_rawDesc = []byte{
 	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, 0x22, 0x81, 0x02, 0x0a, 0x06, 0x41, 0x63, 0x63, 0x65,
+	0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x81, 0x02, 0x0a, 0x06, 0x41, 0x63, 0x63, 0x65,
 	0x73, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 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, 0x23, 0x0a, 0x0d,
@@ -1104,7 +1155,7 @@ var file_collections_collections_proto_rawDesc = []byte{
 	0x0e, 0x64, 0x65, 0x6e, 0x79, 0x52, 0x65, 0x61, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12,
 	0x2a, 0x0a, 0x11, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x66, 0x69,
 	0x65, 0x6c, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x6e, 0x79,
-	0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0xae, 0x06, 0x0a, 0x0a,
+	0x57, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0xf2, 0x07, 0x0a, 0x0a,
 	0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 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,
@@ -1132,137 +1183,150 @@ var file_collections_collections_proto_rawDesc = []byte{
 	0x12, 0x33, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b,
 	0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
 	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x06, 0x61,
-	0x63, 0x63, 0x65, 0x73, 0x73, 0x1a, 0x75, 0x0a, 0x04, 0x56, 0x69, 0x65, 0x77, 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, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x97, 0x01, 0x0a,
-	0x09, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74,
-	0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65,
-	0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x39, 0x0a, 0x0a, 0x73,
-	0x74, 0x61, 0x72, 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, 0x73, 0x74, 0x61,
-	0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x42, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
-	0x07, 0x0a, 0x03, 0x4e, 0x45, 0x57, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x45, 0x50,
-	0x41, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59,
-	0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x0b, 0x0a,
-	0x07, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x04, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73,
-	0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6e, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x22, 0x50, 0x0a, 0x0d,
-	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a,
-	0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
-	0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4b,
-	0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x39, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
-	0x6f, 0x6e, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x44, 0x0a, 0x0a, 0x47,
-	0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x64, 0x69, 0x73,
-	0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x69, 0x6e, 0x63, 0x6c,
-	0x75, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x64, 0x69, 0x73, 0x61,
-	0x62, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65,
-	0x73, 0x22, 0x9e, 0x01, 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, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47,
-	0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x22, 0x4e, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+	0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x69,
+	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x52,
+	0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f,
+	0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x0c, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a,
+	0x0c, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x74, 0x6c, 0x18, 0x20, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b,
+	0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x74, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x6e,
+	0x6f, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x09, 0x6e, 0x6f, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f,
+	0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
+	0x6e, 0x6f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x1a, 0x75, 0x0a, 0x04, 0x56, 0x69, 0x65,
+	0x77, 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, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74,
+	0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
+	0x1a, 0x97, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3b,
+	0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53,
+	0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69,
+	0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12,
+	0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 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, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x42, 0x0a, 0x05, 0x53, 0x74,
+	0x61, 0x74, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x45, 0x57, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09,
+	0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52,
+	0x45, 0x41, 0x44, 0x59, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10,
+	0x03, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x04, 0x42, 0x09,
+	0x0a, 0x07, 0x5f, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6e, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61,
+	0x22, 0x50, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
 	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
 	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c,
 	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
-	0x6f, 0x6e, 0x22, 0xa5, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 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, 0x3f, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x05,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63,
-	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66,
-	0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0xa2, 0x01, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72,
-	0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74,
-	0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64,
-	0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75,
-	0x64, 0x65, 0x5f, 0x6e, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x12,
-	0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68, 0x69, 0x64, 0x64, 0x65,
-	0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65,
-	0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
-	0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
-	0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x51, 0x0a, 0x0c, 0x4c, 0x69,
-	0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0b, 0x63, 0x6f,
-	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
-	0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x50, 0x0a,
-	0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f,
-	0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c,
-	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
-	0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22,
-	0x81, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 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, 0x16, 0x0a, 0x06, 0x73,
-	0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68,
-	0x65, 0x6d, 0x61, 0x22, 0x66, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71,
+	0x6f, 0x6e, 0x22, 0x4b, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c,
+	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22,
+	0x44, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x36, 0x0a,
+	0x17, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f,
+	0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15,
+	0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x49, 0x6e, 0x63,
+	0x6c, 0x75, 0x64, 0x65, 0x73, 0x22, 0x9e, 0x01, 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, 0x32, 0xdb, 0x03, 0x0a, 0x0b,
-	0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x53, 0x0a, 0x06, 0x43,
-	0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x07, 0x6f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63,
+	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4e, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+	0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c,
+	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 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, 0x3f, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74,
+	0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c,
+	0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65,
+	0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0xa2, 0x01, 0x0a, 0x06, 0x46, 0x69,
+	0x6c, 0x74, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f,
+	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x78,
+	0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x26, 0x0a, 0x0f, 0x69,
+	0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x6e, 0x6f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x6f, 0x44,
+	0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x68,
+	0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x63,
+	0x6c, 0x75, 0x64, 0x65, 0x48, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e,
+	0x0a, 0x02, 0x69, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x51,
+	0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41,
+	0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f,
+	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x22, 0x50, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6c,
+	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x22, 0x81, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d,
+	0x61, 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,
+	0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x66, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 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, 0x32,
+	0xdb, 0x03, 0x0a, 0x0b, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+	0x53, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
 	0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
-	0x12, 0x4a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
-	0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65,
-	0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47,
-	0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x04,
-	0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63,
-	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73,
-	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x06, 0x55,
-	0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 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, 0x4c, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
-	0x12, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
-	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
-	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, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x63, 0x6f,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1f, 0x2e, 0x63, 0x6f,
 	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	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, 0x42, 0x3c, 0x5a, 0x3a, 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, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x63, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x63,
+	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
+	0x12, 0x4d, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c,
+	0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+	0x46, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 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, 0x4c, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x53, 0x63,
+	0x68, 0x65, 0x6d, 0x61, 0x12, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63,
+	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x63,
+	0x68, 0x65, 0x6d, 0x61, 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, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
+	0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
+	0x74, 0x69, 0x6f, 0x6e, 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, 0x42, 0x3c, 0x5a,
+	0x3a, 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, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3b,
+	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1279,7 +1343,7 @@ func file_collections_collections_proto_rawDescGZIP() []byte {
 
 var file_collections_collections_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_collections_collections_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
-var file_collections_collections_proto_goTypes = []interface{}{
+var file_collections_collections_proto_goTypes = []any{
 	(Collection_State)(0),         // 0: content.collections.Collection.State
 	(*Access)(nil),                // 1: content.collections.Access
 	(*Collection)(nil),            // 2: content.collections.Collection
@@ -1297,40 +1361,42 @@ var file_collections_collections_proto_goTypes = []interface{}{
 	(*Collection_StateInfo)(nil),  // 14: content.collections.Collection.StateInfo
 	(*ListRequest_Filter)(nil),    // 15: content.collections.ListRequest.Filter
 	(common.Action)(0),            // 16: common.Action
-	(*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp
-	(*emptypb.Empty)(nil),         // 18: google.protobuf.Empty
+	(*durationpb.Duration)(nil),   // 17: google.protobuf.Duration
+	(*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
+	(*emptypb.Empty)(nil),         // 19: google.protobuf.Empty
 }
 var file_collections_collections_proto_depIdxs = []int32{
 	16, // 0: content.collections.Access.actions:type_name -> common.Action
 	13, // 1: content.collections.Collection.view:type_name -> content.collections.Collection.View
 	14, // 2: content.collections.Collection.state_info:type_name -> content.collections.Collection.StateInfo
 	1,  // 3: content.collections.Collection.access:type_name -> content.collections.Access
-	2,  // 4: content.collections.CreateRequest.collection:type_name -> content.collections.Collection
-	2,  // 5: content.collections.CreateResponse.created:type_name -> content.collections.Collection
-	5,  // 6: content.collections.GetRequest.options:type_name -> content.collections.GetOptions
-	2,  // 7: content.collections.GetResponse.collection:type_name -> content.collections.Collection
-	15, // 8: content.collections.ListRequest.filter:type_name -> content.collections.ListRequest.Filter
-	2,  // 9: content.collections.ListResponse.collections:type_name -> content.collections.Collection
-	2,  // 10: content.collections.UpdateRequest.collection:type_name -> content.collections.Collection
-	0,  // 11: content.collections.Collection.StateInfo.state:type_name -> content.collections.Collection.State
-	17, // 12: content.collections.Collection.StateInfo.started_at:type_name -> google.protobuf.Timestamp
-	3,  // 13: content.collections.Collections.Create:input_type -> content.collections.CreateRequest
-	6,  // 14: content.collections.Collections.Get:input_type -> content.collections.GetRequest
-	8,  // 15: content.collections.Collections.List:input_type -> content.collections.ListRequest
-	10, // 16: content.collections.Collections.Update:input_type -> content.collections.UpdateRequest
-	11, // 17: content.collections.Collections.SetSchema:input_type -> content.collections.SetSchemaRequest
-	12, // 18: content.collections.Collections.Delete:input_type -> content.collections.DeleteRequest
-	4,  // 19: content.collections.Collections.Create:output_type -> content.collections.CreateResponse
-	7,  // 20: content.collections.Collections.Get:output_type -> content.collections.GetResponse
-	9,  // 21: content.collections.Collections.List:output_type -> content.collections.ListResponse
-	18, // 22: content.collections.Collections.Update:output_type -> google.protobuf.Empty
-	18, // 23: content.collections.Collections.SetSchema:output_type -> google.protobuf.Empty
-	18, // 24: content.collections.Collections.Delete:output_type -> google.protobuf.Empty
-	19, // [19:25] is the sub-list for method output_type
-	13, // [13:19] is the sub-list for method input_type
-	13, // [13:13] is the sub-list for extension type_name
-	13, // [13:13] is the sub-list for extension extendee
-	0,  // [0:13] is the sub-list for field type_name
+	17, // 4: content.collections.Collection.revision_ttl:type_name -> google.protobuf.Duration
+	2,  // 5: content.collections.CreateRequest.collection:type_name -> content.collections.Collection
+	2,  // 6: content.collections.CreateResponse.created:type_name -> content.collections.Collection
+	5,  // 7: content.collections.GetRequest.options:type_name -> content.collections.GetOptions
+	2,  // 8: content.collections.GetResponse.collection:type_name -> content.collections.Collection
+	15, // 9: content.collections.ListRequest.filter:type_name -> content.collections.ListRequest.Filter
+	2,  // 10: content.collections.ListResponse.collections:type_name -> content.collections.Collection
+	2,  // 11: content.collections.UpdateRequest.collection:type_name -> content.collections.Collection
+	0,  // 12: content.collections.Collection.StateInfo.state:type_name -> content.collections.Collection.State
+	18, // 13: content.collections.Collection.StateInfo.started_at:type_name -> google.protobuf.Timestamp
+	3,  // 14: content.collections.Collections.Create:input_type -> content.collections.CreateRequest
+	6,  // 15: content.collections.Collections.Get:input_type -> content.collections.GetRequest
+	8,  // 16: content.collections.Collections.List:input_type -> content.collections.ListRequest
+	10, // 17: content.collections.Collections.Update:input_type -> content.collections.UpdateRequest
+	11, // 18: content.collections.Collections.SetSchema:input_type -> content.collections.SetSchemaRequest
+	12, // 19: content.collections.Collections.Delete:input_type -> content.collections.DeleteRequest
+	4,  // 20: content.collections.Collections.Create:output_type -> content.collections.CreateResponse
+	7,  // 21: content.collections.Collections.Get:output_type -> content.collections.GetResponse
+	9,  // 22: content.collections.Collections.List:output_type -> content.collections.ListResponse
+	19, // 23: content.collections.Collections.Update:output_type -> google.protobuf.Empty
+	19, // 24: content.collections.Collections.SetSchema:output_type -> google.protobuf.Empty
+	19, // 25: content.collections.Collections.Delete:output_type -> google.protobuf.Empty
+	20, // [20:26] is the sub-list for method output_type
+	14, // [14:20] is the sub-list for method input_type
+	14, // [14:14] is the sub-list for extension type_name
+	14, // [14:14] is the sub-list for extension extendee
+	0,  // [0:14] is the sub-list for field type_name
 }
 
 func init() { file_collections_collections_proto_init() }
@@ -1339,7 +1405,7 @@ func file_collections_collections_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_collections_collections_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Access); i {
 			case 0:
 				return &v.state
@@ -1351,7 +1417,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Collection); i {
 			case 0:
 				return &v.state
@@ -1363,7 +1429,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -1375,7 +1441,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -1387,7 +1453,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*GetOptions); i {
 			case 0:
 				return &v.state
@@ -1399,7 +1465,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -1411,7 +1477,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -1423,7 +1489,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest); i {
 			case 0:
 				return &v.state
@@ -1435,7 +1501,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*ListResponse); i {
 			case 0:
 				return &v.state
@@ -1447,7 +1513,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -1459,7 +1525,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*SetSchemaRequest); i {
 			case 0:
 				return &v.state
@@ -1471,7 +1537,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -1483,7 +1549,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*Collection_View); i {
 			case 0:
 				return &v.state
@@ -1495,7 +1561,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*Collection_StateInfo); i {
 			case 0:
 				return &v.state
@@ -1507,7 +1573,7 @@ func file_collections_collections_proto_init() {
 				return nil
 			}
 		}
-		file_collections_collections_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_collections_collections_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest_Filter); i {
 			case 0:
 				return &v.state
@@ -1520,7 +1586,7 @@ func file_collections_collections_proto_init() {
 			}
 		}
 	}
-	file_collections_collections_proto_msgTypes[1].OneofWrappers = []interface{}{}
+	file_collections_collections_proto_msgTypes[1].OneofWrappers = []any{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
diff --git a/proto/collections/collections_grpc.pb.go b/proto/collections/collections_grpc.pb.go
index 392ef1841c36ee9ca29d6ccc3bfffd9e97ec0054..8d5bc339a5bdba376478a59bb88df68fbf5566c8 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.25.1
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: collections/collections.proto
 
 package collections
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Collections_Create_FullMethodName    = "/content.collections.Collections/Create"
@@ -63,8 +63,9 @@ func NewCollectionsClient(cc grpc.ClientConnInterface) CollectionsClient {
 }
 
 func (c *collectionsClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Collections_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collections_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -72,8 +73,9 @@ func (c *collectionsClient) Create(ctx context.Context, in *CreateRequest, opts
 }
 
 func (c *collectionsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Collections_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collections_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -81,8 +83,9 @@ func (c *collectionsClient) Get(ctx context.Context, in *GetRequest, opts ...grp
 }
 
 func (c *collectionsClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListResponse)
-	err := c.cc.Invoke(ctx, Collections_List_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collections_List_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -90,8 +93,9 @@ func (c *collectionsClient) List(ctx context.Context, in *ListRequest, opts ...g
 }
 
 func (c *collectionsClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Collections_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collections_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -99,8 +103,9 @@ func (c *collectionsClient) Update(ctx context.Context, in *UpdateRequest, opts
 }
 
 func (c *collectionsClient) SetSchema(ctx context.Context, in *SetSchemaRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Collections_SetSchema_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collections_SetSchema_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -108,8 +113,9 @@ func (c *collectionsClient) SetSchema(ctx context.Context, in *SetSchemaRequest,
 }
 
 func (c *collectionsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Collections_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Collections_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -118,7 +124,7 @@ func (c *collectionsClient) Delete(ctx context.Context, in *DeleteRequest, opts
 
 // CollectionsServer is the server API for Collections service.
 // All implementations must embed UnimplementedCollectionsServer
-// for forward compatibility
+// for forward compatibility.
 type CollectionsServer interface {
 	// Создать коллекцию. Установка схемы производится через отдельный метод `SetSchema` и методом `Create` игнорируется
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
@@ -143,9 +149,12 @@ type CollectionsServer interface {
 	mustEmbedUnimplementedCollectionsServer()
 }
 
-// UnimplementedCollectionsServer must be embedded to have forward compatible implementations.
-type UnimplementedCollectionsServer struct {
-}
+// UnimplementedCollectionsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedCollectionsServer struct{}
 
 func (UnimplementedCollectionsServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -166,6 +175,7 @@ func (UnimplementedCollectionsServer) Delete(context.Context, *DeleteRequest) (*
 	return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
 }
 func (UnimplementedCollectionsServer) mustEmbedUnimplementedCollectionsServer() {}
+func (UnimplementedCollectionsServer) testEmbeddedByValue()                     {}
 
 // UnsafeCollectionsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to CollectionsServer will
@@ -175,6 +185,13 @@ type UnsafeCollectionsServer interface {
 }
 
 func RegisterCollectionsServer(s grpc.ServiceRegistrar, srv CollectionsServer) {
+	// If the following call pancis, it indicates UnimplementedCollectionsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Collections_ServiceDesc, srv)
 }
 
diff --git a/proto/common/common.pb.go b/proto/common/common.pb.go
index ea585fe3435096d877fa4bbc3649fe96a4b0649f..3495031fb399fabaa59992460d63f40654e69f3e 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.31.0
-// 	protoc        v4.25.1
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: common/common.proto
 
 package common
@@ -125,6 +125,9 @@ func (Action) EnumDescriptor() ([]byte, []int) {
 	return file_common_common_proto_rawDescGZIP(), []int{1}
 }
 
+// Deprecated
+// Поддержка этой реализации фильтра осталась только в запросе поиска записей (Items.Find)
+// Возможно, его поддержка будет прекращена в следующих версиях системы
 type Filter struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -639,7 +642,7 @@ 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, 5)
-var file_common_common_proto_goTypes = []interface{}{
+var file_common_common_proto_goTypes = []any{
 	(Access)(0),            // 0: common.Access
 	(Action)(0),            // 1: common.Action
 	(*Filter)(nil),         // 2: common.Filter
@@ -666,7 +669,7 @@ func file_common_common_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_common_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_common_common_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -678,7 +681,7 @@ func file_common_common_proto_init() {
 				return nil
 			}
 		}
-		file_common_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_common_common_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*FindOptions); i {
 			case 0:
 				return &v.state
@@ -690,7 +693,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[2].Exporter = func(v any, i int) any {
 			switch v := v.(*Rule); i {
 			case 0:
 				return &v.state
@@ -702,7 +705,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[3].Exporter = func(v any, i int) any {
 			switch v := v.(*Collaborator); i {
 			case 0:
 				return &v.state
@@ -714,7 +717,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[4].Exporter = func(v any, i int) any {
 			switch v := v.(*Version); i {
 			case 0:
 				return &v.state
diff --git a/proto/common/error.pb.go b/proto/common/error.pb.go
index 444bea97b4c027067c4675b3d606a3f9c85f64b8..6eeb8c55eed7f7c04c9882dc2f8d8537da01b4be 100644
--- a/proto/common/error.pb.go
+++ b/proto/common/error.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: common/error.proto
 
 package common
@@ -556,7 +556,7 @@ func file_common_error_proto_rawDescGZIP() []byte {
 }
 
 var file_common_error_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
-var file_common_error_proto_goTypes = []interface{}{
+var file_common_error_proto_goTypes = []any{
 	(*Error)(nil),                           // 0: common.Error
 	(*Error_BadRequest)(nil),                // 1: common.Error.BadRequest
 	(*Error_Help)(nil),                      // 2: common.Error.Help
@@ -588,7 +588,7 @@ func file_common_error_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_common_error_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Error); i {
 			case 0:
 				return &v.state
@@ -600,7 +600,7 @@ func file_common_error_proto_init() {
 				return nil
 			}
 		}
-		file_common_error_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Error_BadRequest); i {
 			case 0:
 				return &v.state
@@ -612,7 +612,7 @@ func file_common_error_proto_init() {
 				return nil
 			}
 		}
-		file_common_error_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*Error_Help); i {
 			case 0:
 				return &v.state
@@ -624,7 +624,7 @@ func file_common_error_proto_init() {
 				return nil
 			}
 		}
-		file_common_error_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*Error_DebugInfo); i {
 			case 0:
 				return &v.state
@@ -636,7 +636,7 @@ func file_common_error_proto_init() {
 				return nil
 			}
 		}
-		file_common_error_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*Error_LocalizedMessage); i {
 			case 0:
 				return &v.state
@@ -648,7 +648,7 @@ func file_common_error_proto_init() {
 				return nil
 			}
 		}
-		file_common_error_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*Error_BadRequest_FieldViolation); i {
 			case 0:
 				return &v.state
@@ -660,7 +660,7 @@ func file_common_error_proto_init() {
 				return nil
 			}
 		}
-		file_common_error_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_common_error_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*Error_Help_Link); i {
 			case 0:
 				return &v.state
diff --git a/proto/common/operation.pb.go b/proto/common/operation.pb.go
index fd84dde80f2875d3282987fb19e8df17aabed780..3f72af24932eec58d77f558c988e5e197605198e 100644
--- a/proto/common/operation.pb.go
+++ b/proto/common/operation.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: common/operation.proto
 
 package common
@@ -224,7 +224,7 @@ func file_common_operation_proto_rawDescGZIP() []byte {
 }
 
 var file_common_operation_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_common_operation_proto_goTypes = []interface{}{
+var file_common_operation_proto_goTypes = []any{
 	(*Operation)(nil),             // 0: common.Operation
 	(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
 	(*anypb.Any)(nil),             // 2: google.protobuf.Any
@@ -250,7 +250,7 @@ func file_common_operation_proto_init() {
 	}
 	file_common_error_proto_init()
 	if !protoimpl.UnsafeEnabled {
-		file_common_operation_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_common_operation_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Operation); i {
 			case 0:
 				return &v.state
@@ -263,7 +263,7 @@ func file_common_operation_proto_init() {
 			}
 		}
 	}
-	file_common_operation_proto_msgTypes[0].OneofWrappers = []interface{}{
+	file_common_operation_proto_msgTypes[0].OneofWrappers = []any{
 		(*Operation_Response)(nil),
 		(*Operation_Error)(nil),
 	}
diff --git a/proto/common/operation_service.pb.go b/proto/common/operation_service.pb.go
index 93876246a4f69845688755a3a88bfde6330cbcd7..d0bd8e57acdc3298dbf9841feef256978805dd07 100644
--- a/proto/common/operation_service.pb.go
+++ b/proto/common/operation_service.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: common/operation_service.proto
 
 package common
@@ -159,7 +159,7 @@ func file_common_operation_service_proto_rawDescGZIP() []byte {
 }
 
 var file_common_operation_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
-var file_common_operation_service_proto_goTypes = []interface{}{
+var file_common_operation_service_proto_goTypes = []any{
 	(*GetOperationRequest)(nil),    // 0: common.GetOperationRequest
 	(*CancelOperationRequest)(nil), // 1: common.CancelOperationRequest
 	(*Operation)(nil),              // 2: common.Operation
@@ -184,7 +184,7 @@ func file_common_operation_service_proto_init() {
 	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{} {
+		file_common_operation_service_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*GetOperationRequest); i {
 			case 0:
 				return &v.state
@@ -196,7 +196,7 @@ func file_common_operation_service_proto_init() {
 				return nil
 			}
 		}
-		file_common_operation_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_common_operation_service_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*CancelOperationRequest); i {
 			case 0:
 				return &v.state
diff --git a/proto/common/operation_service_grpc.pb.go b/proto/common/operation_service_grpc.pb.go
index d323b0be496411363ba80de50f70246382663307..ff590c80c97d7d31863c2643c93bb359c3bfec92 100644
--- a/proto/common/operation_service_grpc.pb.go
+++ b/proto/common/operation_service_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.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: common/operation_service.proto
 
 package common
@@ -15,8 +15,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	OperationService_Get_FullMethodName    = "/common.OperationService/Get"
@@ -42,8 +42,9 @@ func NewOperationServiceClient(cc grpc.ClientConnInterface) OperationServiceClie
 }
 
 func (c *operationServiceClient) Get(ctx context.Context, in *GetOperationRequest, opts ...grpc.CallOption) (*Operation, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(Operation)
-	err := c.cc.Invoke(ctx, OperationService_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, OperationService_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -51,8 +52,9 @@ func (c *operationServiceClient) Get(ctx context.Context, in *GetOperationReques
 }
 
 func (c *operationServiceClient) Cancel(ctx context.Context, in *CancelOperationRequest, opts ...grpc.CallOption) (*Operation, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(Operation)
-	err := c.cc.Invoke(ctx, OperationService_Cancel_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, OperationService_Cancel_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -61,7 +63,7 @@ func (c *operationServiceClient) Cancel(ctx context.Context, in *CancelOperation
 
 // OperationServiceServer is the server API for OperationService service.
 // All implementations must embed UnimplementedOperationServiceServer
-// for forward compatibility
+// for forward compatibility.
 type OperationServiceServer interface {
 	// Возвращает статус операции и ее результат, если она завершена
 	Get(context.Context, *GetOperationRequest) (*Operation, error)
@@ -70,9 +72,12 @@ type OperationServiceServer interface {
 	mustEmbedUnimplementedOperationServiceServer()
 }
 
-// UnimplementedOperationServiceServer must be embedded to have forward compatible implementations.
-type UnimplementedOperationServiceServer struct {
-}
+// UnimplementedOperationServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedOperationServiceServer struct{}
 
 func (UnimplementedOperationServiceServer) Get(context.Context, *GetOperationRequest) (*Operation, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
@@ -81,6 +86,7 @@ func (UnimplementedOperationServiceServer) Cancel(context.Context, *CancelOperat
 	return nil, status.Errorf(codes.Unimplemented, "method Cancel not implemented")
 }
 func (UnimplementedOperationServiceServer) mustEmbedUnimplementedOperationServiceServer() {}
+func (UnimplementedOperationServiceServer) testEmbeddedByValue()                          {}
 
 // 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
@@ -90,6 +96,13 @@ type UnsafeOperationServiceServer interface {
 }
 
 func RegisterOperationServiceServer(s grpc.ServiceRegistrar, srv OperationServiceServer) {
+	// If the following call pancis, it indicates UnimplementedOperationServiceServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&OperationService_ServiceDesc, srv)
 }
 
diff --git a/proto/common/validation.pb.go b/proto/common/validation.pb.go
index cca43ab540da1592a3bb4c21a91ba2be6f1d22bb..8fe6a0c097dea4ef9b71217c94dbbd309de0f55d 100644
--- a/proto/common/validation.pb.go
+++ b/proto/common/validation.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: common/validation.proto
 
 package common
@@ -251,7 +251,7 @@ func file_common_validation_proto_rawDescGZIP() []byte {
 }
 
 var file_common_validation_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_common_validation_proto_goTypes = []interface{}{
+var file_common_validation_proto_goTypes = []any{
 	(*MapKeySpec)(nil),                // 0: common.MapKeySpec
 	(*descriptorpb.OneofOptions)(nil), // 1: google.protobuf.OneofOptions
 	(*descriptorpb.FieldOptions)(nil), // 2: google.protobuf.FieldOptions
@@ -280,7 +280,7 @@ func file_common_validation_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_common_validation_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_common_validation_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*MapKeySpec); i {
 			case 0:
 				return &v.state
diff --git a/proto/delivery/delivery.pb.go b/proto/delivery/delivery.pb.go
index a8673b3e6f8ded57f397a25ca78ae7538d3351ee..81f70e6644e803bf9e33bcce1f6f1d5ea1d73cf7 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: delivery/delivery.proto
 
 package delivery
@@ -1144,7 +1144,7 @@ func file_delivery_delivery_proto_rawDescGZIP() []byte {
 }
 
 var file_delivery_delivery_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
-var file_delivery_delivery_proto_goTypes = []interface{}{
+var file_delivery_delivery_proto_goTypes = []any{
 	(*ListLocalesRequest)(nil),         // 0: delivery.ListLocalesRequest
 	(*ListLocalesResponse)(nil),        // 1: delivery.ListLocalesResponse
 	(*GetEnvironmentRequest)(nil),      // 2: delivery.GetEnvironmentRequest
@@ -1216,7 +1216,7 @@ func file_delivery_delivery_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_delivery_delivery_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*ListLocalesRequest); i {
 			case 0:
 				return &v.state
@@ -1228,7 +1228,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*ListLocalesResponse); i {
 			case 0:
 				return &v.state
@@ -1240,7 +1240,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*GetEnvironmentRequest); i {
 			case 0:
 				return &v.state
@@ -1252,7 +1252,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*GetEnvironmentResponse); i {
 			case 0:
 				return &v.state
@@ -1264,7 +1264,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*ListEnvironmentsRequest); i {
 			case 0:
 				return &v.state
@@ -1276,7 +1276,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*ListEnvironmentsResponse); i {
 			case 0:
 				return &v.state
@@ -1288,7 +1288,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*GetCollectionRequest); i {
 			case 0:
 				return &v.state
@@ -1300,7 +1300,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*GetCollectionResponse); i {
 			case 0:
 				return &v.state
@@ -1312,7 +1312,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*ListCollectionsRequest); i {
 			case 0:
 				return &v.state
@@ -1324,7 +1324,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*ListCollectionsResponse); i {
 			case 0:
 				return &v.state
@@ -1336,7 +1336,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*GetItemRequest); i {
 			case 0:
 				return &v.state
@@ -1348,7 +1348,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*GetItemResponse); i {
 			case 0:
 				return &v.state
@@ -1360,7 +1360,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*FindItemsRequest); i {
 			case 0:
 				return &v.state
@@ -1372,7 +1372,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*FindItemsResponse); i {
 			case 0:
 				return &v.state
@@ -1384,7 +1384,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregateOptions); i {
 			case 0:
 				return &v.state
@@ -1396,7 +1396,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[15].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregateRequest); i {
 			case 0:
 				return &v.state
@@ -1408,7 +1408,7 @@ func file_delivery_delivery_proto_init() {
 				return nil
 			}
 		}
-		file_delivery_delivery_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+		file_delivery_delivery_proto_msgTypes[16].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregateResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/delivery/delivery_grpc.pb.go b/proto/delivery/delivery_grpc.pb.go
index 3840ca0fc32236f38b8c92eac59c56994996be23..a0a14e9b14e4cc3d0fb7e1bfeb0d418b708b998d 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: delivery/delivery.proto
 
 package delivery
@@ -15,8 +15,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Delivery_ListLocales_FullMethodName      = "/delivery.Delivery/ListLocales"
@@ -52,8 +52,9 @@ func NewDeliveryClient(cc grpc.ClientConnInterface) DeliveryClient {
 }
 
 func (c *deliveryClient) ListLocales(ctx context.Context, in *ListLocalesRequest, opts ...grpc.CallOption) (*ListLocalesResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListLocalesResponse)
-	err := c.cc.Invoke(ctx, Delivery_ListLocales_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_ListLocales_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -61,8 +62,9 @@ func (c *deliveryClient) ListLocales(ctx context.Context, in *ListLocalesRequest
 }
 
 func (c *deliveryClient) GetEnvironment(ctx context.Context, in *GetEnvironmentRequest, opts ...grpc.CallOption) (*GetEnvironmentResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetEnvironmentResponse)
-	err := c.cc.Invoke(ctx, Delivery_GetEnvironment_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_GetEnvironment_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -70,8 +72,9 @@ func (c *deliveryClient) GetEnvironment(ctx context.Context, in *GetEnvironmentR
 }
 
 func (c *deliveryClient) ListEnvironments(ctx context.Context, in *ListEnvironmentsRequest, opts ...grpc.CallOption) (*ListEnvironmentsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListEnvironmentsResponse)
-	err := c.cc.Invoke(ctx, Delivery_ListEnvironments_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_ListEnvironments_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -79,8 +82,9 @@ func (c *deliveryClient) ListEnvironments(ctx context.Context, in *ListEnvironme
 }
 
 func (c *deliveryClient) GetCollection(ctx context.Context, in *GetCollectionRequest, opts ...grpc.CallOption) (*GetCollectionResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetCollectionResponse)
-	err := c.cc.Invoke(ctx, Delivery_GetCollection_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_GetCollection_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -88,8 +92,9 @@ func (c *deliveryClient) GetCollection(ctx context.Context, in *GetCollectionReq
 }
 
 func (c *deliveryClient) ListCollections(ctx context.Context, in *ListCollectionsRequest, opts ...grpc.CallOption) (*ListCollectionsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListCollectionsResponse)
-	err := c.cc.Invoke(ctx, Delivery_ListCollections_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_ListCollections_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -97,8 +102,9 @@ func (c *deliveryClient) ListCollections(ctx context.Context, in *ListCollection
 }
 
 func (c *deliveryClient) GetItem(ctx context.Context, in *GetItemRequest, opts ...grpc.CallOption) (*GetItemResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetItemResponse)
-	err := c.cc.Invoke(ctx, Delivery_GetItem_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_GetItem_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -106,8 +112,9 @@ func (c *deliveryClient) GetItem(ctx context.Context, in *GetItemRequest, opts .
 }
 
 func (c *deliveryClient) FindItems(ctx context.Context, in *FindItemsRequest, opts ...grpc.CallOption) (*FindItemsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindItemsResponse)
-	err := c.cc.Invoke(ctx, Delivery_FindItems_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_FindItems_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -115,8 +122,9 @@ func (c *deliveryClient) FindItems(ctx context.Context, in *FindItemsRequest, op
 }
 
 func (c *deliveryClient) Aggregate(ctx context.Context, in *AggregateRequest, opts ...grpc.CallOption) (*AggregateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(AggregateResponse)
-	err := c.cc.Invoke(ctx, Delivery_Aggregate_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Delivery_Aggregate_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -125,7 +133,7 @@ func (c *deliveryClient) Aggregate(ctx context.Context, in *AggregateRequest, op
 
 // DeliveryServer is the server API for Delivery service.
 // All implementations must embed UnimplementedDeliveryServer
-// for forward compatibility
+// for forward compatibility.
 type DeliveryServer interface {
 	ListLocales(context.Context, *ListLocalesRequest) (*ListLocalesResponse, error)
 	GetEnvironment(context.Context, *GetEnvironmentRequest) (*GetEnvironmentResponse, error)
@@ -138,9 +146,12 @@ type DeliveryServer interface {
 	mustEmbedUnimplementedDeliveryServer()
 }
 
-// UnimplementedDeliveryServer must be embedded to have forward compatible implementations.
-type UnimplementedDeliveryServer struct {
-}
+// UnimplementedDeliveryServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedDeliveryServer struct{}
 
 func (UnimplementedDeliveryServer) ListLocales(context.Context, *ListLocalesRequest) (*ListLocalesResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ListLocales not implemented")
@@ -167,6 +178,7 @@ func (UnimplementedDeliveryServer) Aggregate(context.Context, *AggregateRequest)
 	return nil, status.Errorf(codes.Unimplemented, "method Aggregate not implemented")
 }
 func (UnimplementedDeliveryServer) mustEmbedUnimplementedDeliveryServer() {}
+func (UnimplementedDeliveryServer) testEmbeddedByValue()                  {}
 
 // UnsafeDeliveryServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to DeliveryServer will
@@ -176,6 +188,13 @@ type UnsafeDeliveryServer interface {
 }
 
 func RegisterDeliveryServer(s grpc.ServiceRegistrar, srv DeliveryServer) {
+	// If the following call pancis, it indicates UnimplementedDeliveryServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Delivery_ServiceDesc, srv)
 }
 
diff --git a/proto/environments/environments.pb.go b/proto/environments/environments.pb.go
index 8dd2d620cd51e0df031c738d8409585481974062..9bff8500c7c303f5cbeec05f36d3ce4c24809b47 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.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: environments/environments.proto
 
 package environments
@@ -1082,7 +1082,7 @@ func file_environments_environments_proto_rawDescGZIP() []byte {
 
 var file_environments_environments_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_environments_environments_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
-var file_environments_environments_proto_goTypes = []interface{}{
+var file_environments_environments_proto_goTypes = []any{
 	(StateInfo_State)(0),          // 0: content.environments.StateInfo.State
 	(*Environment)(nil),           // 1: content.environments.Environment
 	(*StateInfo)(nil),             // 2: content.environments.StateInfo
@@ -1142,7 +1142,7 @@ func file_environments_environments_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_environments_environments_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Environment); i {
 			case 0:
 				return &v.state
@@ -1154,7 +1154,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*StateInfo); i {
 			case 0:
 				return &v.state
@@ -1166,7 +1166,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*Config); i {
 			case 0:
 				return &v.state
@@ -1178,7 +1178,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -1190,7 +1190,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -1202,7 +1202,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest); i {
 			case 0:
 				return &v.state
@@ -1214,7 +1214,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*ListResponse); i {
 			case 0:
 				return &v.state
@@ -1226,7 +1226,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -1238,7 +1238,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -1250,7 +1250,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -1262,7 +1262,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*SetAliasRequest); i {
 			case 0:
 				return &v.state
@@ -1274,7 +1274,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*RemoveAliasRequest); i {
 			case 0:
 				return &v.state
@@ -1286,7 +1286,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -1298,7 +1298,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*MigrateRequest); i {
 			case 0:
 				return &v.state
@@ -1310,7 +1310,7 @@ func file_environments_environments_proto_init() {
 				return nil
 			}
 		}
-		file_environments_environments_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_environments_environments_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*MigrateOptions); i {
 			case 0:
 				return &v.state
diff --git a/proto/environments/environments_grpc.pb.go b/proto/environments/environments_grpc.pb.go
index 63268a0ba508b1e00b189a7588573fd12c47c0fb..b0d65500e40a5822a0675deb061db968742dc955 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.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: environments/environments.proto
 
 package environments
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Environments_Create_FullMethodName      = "/content.environments.Environments/Create"
@@ -60,8 +60,9 @@ func NewEnvironmentsClient(cc grpc.ClientConnInterface) EnvironmentsClient {
 }
 
 func (c *environmentsClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Environments_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -69,8 +70,9 @@ func (c *environmentsClient) Create(ctx context.Context, in *CreateRequest, opts
 }
 
 func (c *environmentsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Environments_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -78,8 +80,9 @@ func (c *environmentsClient) Get(ctx context.Context, in *GetRequest, opts ...gr
 }
 
 func (c *environmentsClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListResponse)
-	err := c.cc.Invoke(ctx, Environments_List_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_List_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -87,8 +90,9 @@ func (c *environmentsClient) List(ctx context.Context, in *ListRequest, opts ...
 }
 
 func (c *environmentsClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Environments_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -96,8 +100,9 @@ func (c *environmentsClient) Update(ctx context.Context, in *UpdateRequest, opts
 }
 
 func (c *environmentsClient) SetAlias(ctx context.Context, in *SetAliasRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Environments_SetAlias_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_SetAlias_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -105,8 +110,9 @@ func (c *environmentsClient) SetAlias(ctx context.Context, in *SetAliasRequest,
 }
 
 func (c *environmentsClient) RemoveAlias(ctx context.Context, in *RemoveAliasRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Environments_RemoveAlias_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_RemoveAlias_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -114,8 +120,9 @@ func (c *environmentsClient) RemoveAlias(ctx context.Context, in *RemoveAliasReq
 }
 
 func (c *environmentsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Environments_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -123,8 +130,9 @@ func (c *environmentsClient) Delete(ctx context.Context, in *DeleteRequest, opts
 }
 
 func (c *environmentsClient) Migrate(ctx context.Context, in *MigrateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Environments_Migrate_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Environments_Migrate_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -133,7 +141,7 @@ func (c *environmentsClient) Migrate(ctx context.Context, in *MigrateRequest, op
 
 // EnvironmentsServer is the server API for Environments service.
 // All implementations must embed UnimplementedEnvironmentsServer
-// for forward compatibility
+// for forward compatibility.
 type EnvironmentsServer interface {
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
 	Get(context.Context, *GetRequest) (*GetResponse, error)
@@ -153,9 +161,12 @@ type EnvironmentsServer interface {
 	mustEmbedUnimplementedEnvironmentsServer()
 }
 
-// UnimplementedEnvironmentsServer must be embedded to have forward compatible implementations.
-type UnimplementedEnvironmentsServer struct {
-}
+// UnimplementedEnvironmentsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedEnvironmentsServer struct{}
 
 func (UnimplementedEnvironmentsServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -182,6 +193,7 @@ func (UnimplementedEnvironmentsServer) Migrate(context.Context, *MigrateRequest)
 	return nil, status.Errorf(codes.Unimplemented, "method Migrate not implemented")
 }
 func (UnimplementedEnvironmentsServer) mustEmbedUnimplementedEnvironmentsServer() {}
+func (UnimplementedEnvironmentsServer) testEmbeddedByValue()                      {}
 
 // UnsafeEnvironmentsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to EnvironmentsServer will
@@ -191,6 +203,13 @@ type UnsafeEnvironmentsServer interface {
 }
 
 func RegisterEnvironmentsServer(s grpc.ServiceRegistrar, srv EnvironmentsServer) {
+	// If the following call pancis, it indicates UnimplementedEnvironmentsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Environments_ServiceDesc, srv)
 }
 
diff --git a/proto/extensions/extension.pb.go b/proto/extensions/extension.pb.go
index 0c1501b6d088570de34d5bb617346f620ed643ef..042f307b258e3fdff0921f06d3b8ca15118c6b61 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: extensions/extension.proto
 
 package extensions
@@ -490,6 +490,8 @@ type ActionRequest struct {
 	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"`
+	// Идентификатор локали в пространстве: поле может использоваться расширением, если действие возможно выполнить на разных локалях
+	LocaleId string `protobuf:"bytes,11050,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`
 	// Поля к которым применимо действие. В случае если действие выполняется из списка записей, содержит перечень
 	// полей которые пользователь выбрал для отображения в интерфейсе.
 	Fields   []string          `protobuf:"bytes,10550,rep,name=fields,proto3" json:"fields,omitempty"`
@@ -582,6 +584,13 @@ func (x *ActionRequest) GetItemIds() []string {
 	return nil
 }
 
+func (x *ActionRequest) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
 func (x *ActionRequest) GetFields() []string {
 	if x != nil {
 		return x.Fields
@@ -675,7 +684,7 @@ var file_extensions_extension_proto_rawDesc = []byte{
 	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,
+	0x54, 0x4f, 0x4d, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x03, 0x22, 0xfd, 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,
@@ -689,34 +698,36 @@ var file_extensions_extension_proto_rawDesc = []byte{
 	0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0xa2, 0x52, 0x20, 0x01, 0x28, 0x09,
 	0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x74, 0x65, 0x6d,
 	0x5f, 0x69, 0x64, 0x73, 0x18, 0xac, 0x52, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x69, 0x74, 0x65,
-	0x6d, 0x49, 0x64, 0x73, 0x12, 0x17, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0xb6,
-	0x52, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x44, 0x0a,
-	0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0xf8, 0x55, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x27, 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, 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, 0x82, 0x56, 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, 0x36, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d,
-	0x73, 0x18, 0x8c, 0x56, 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, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 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, 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,
+	0x6d, 0x49, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0xaa, 0x56, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
+	0x49, 0x64, 0x12, 0x17, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0xb6, 0x52, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x44, 0x0a, 0x08, 0x6d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0xf8, 0x55, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27,
+	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, 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, 0x82, 0x56, 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, 0x36, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18,
+	0x8c, 0x56, 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, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 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, 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 (
@@ -733,7 +744,7 @@ func file_extensions_extension_proto_rawDescGZIP() []byte {
 
 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{}{
+var file_extensions_extension_proto_goTypes = []any{
 	(Target)(0),                  // 0: extensions.Target
 	(Action_Kind)(0),             // 1: extensions.Action.Kind
 	(Action_View)(0),             // 2: extensions.Action.View
@@ -765,7 +776,7 @@ func file_extensions_extension_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_extensions_extension_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_extension_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Action); i {
 			case 0:
 				return &v.state
@@ -777,7 +788,7 @@ func file_extensions_extension_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_extension_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_extension_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*ActionRequest); i {
 			case 0:
 				return &v.state
diff --git a/proto/extensions/extension_service.pb.go b/proto/extensions/extension_service.pb.go
index ef0d0a82baa9f2683c12b24d8ca85e917904d655..39424e069bb4ea0d1e6efeb6b44542fd8c2a6827 100644
--- a/proto/extensions/extension_service.pb.go
+++ b/proto/extensions/extension_service.pb.go
@@ -10,8 +10,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: extensions/extension_service.proto
 
 package extensions
@@ -582,7 +582,7 @@ func file_extensions_extension_service_proto_rawDescGZIP() []byte {
 
 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{}{
+var file_extensions_extension_service_proto_goTypes = []any{
 	(ActionResponse_State)(0),    // 0: extensions.ActionResponse.State
 	(ActionResponse_Format)(0),   // 1: extensions.ActionResponse.Format
 	(*InstallRequest)(nil),       // 2: extensions.InstallRequest
@@ -625,7 +625,7 @@ func file_extensions_extension_service_proto_init() {
 	}
 	file_extensions_extension_proto_init()
 	if !protoimpl.UnsafeEnabled {
-		file_extensions_extension_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_extension_service_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*InstallRequest); i {
 			case 0:
 				return &v.state
@@ -637,7 +637,7 @@ func file_extensions_extension_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_extension_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_extension_service_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*UninstallRequest); i {
 			case 0:
 				return &v.state
@@ -649,7 +649,7 @@ func file_extensions_extension_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_extension_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_extension_service_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CheckRequest); i {
 			case 0:
 				return &v.state
@@ -661,7 +661,7 @@ func file_extensions_extension_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_extension_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_extension_service_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*ActionResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/extensions/extension_service_grpc.pb.go b/proto/extensions/extension_service_grpc.pb.go
index 3cd37b839f83963d40818ddb7f094894c56f2f12..a5a1a98180108b1c0663862db4150aec9a64ff24 100644
--- a/proto/extensions/extension_service_grpc.pb.go
+++ b/proto/extensions/extension_service_grpc.pb.go
@@ -10,8 +10,8 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: extensions/extension_service.proto
 
 package extensions
@@ -26,8 +26,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	ExtensionService_Install_FullMethodName   = "/extensions.ExtensionService/Install"
@@ -39,6 +39,9 @@ const (
 // 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.
+//
+// ExtensionService - API расширения
+// Сервис должен реализовывать методы `common.OperationService` для работы операциями.
 type ExtensionServiceClient interface {
 	// Install выполняет установку указанных расширений. Если
 	// расширение уже установлены выполняет процесс переустановки. Возвращает longtime операцию
@@ -79,8 +82,9 @@ func NewExtensionServiceClient(cc grpc.ClientConnInterface) ExtensionServiceClie
 }
 
 func (c *extensionServiceClient) Install(ctx context.Context, in *InstallRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(common.Operation)
-	err := c.cc.Invoke(ctx, ExtensionService_Install_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionService_Install_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -88,8 +92,9 @@ func (c *extensionServiceClient) Install(ctx context.Context, in *InstallRequest
 }
 
 func (c *extensionServiceClient) Uninstall(ctx context.Context, in *UninstallRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(common.Operation)
-	err := c.cc.Invoke(ctx, ExtensionService_Uninstall_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionService_Uninstall_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -97,8 +102,9 @@ func (c *extensionServiceClient) Uninstall(ctx context.Context, in *UninstallReq
 }
 
 func (c *extensionServiceClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(common.Operation)
-	err := c.cc.Invoke(ctx, ExtensionService_Check_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionService_Check_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -106,8 +112,9 @@ func (c *extensionServiceClient) Check(ctx context.Context, in *CheckRequest, op
 }
 
 func (c *extensionServiceClient) Action(ctx context.Context, in *ActionRequest, opts ...grpc.CallOption) (*ActionResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ActionResponse)
-	err := c.cc.Invoke(ctx, ExtensionService_Action_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionService_Action_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -116,7 +123,10 @@ func (c *extensionServiceClient) Action(ctx context.Context, in *ActionRequest,
 
 // ExtensionServiceServer is the server API for ExtensionService service.
 // All implementations must embed UnimplementedExtensionServiceServer
-// for forward compatibility
+// for forward compatibility.
+//
+// ExtensionService - API расширения
+// Сервис должен реализовывать методы `common.OperationService` для работы операциями.
 type ExtensionServiceServer interface {
 	// Install выполняет установку указанных расширений. Если
 	// расширение уже установлены выполняет процесс переустановки. Возвращает longtime операцию
@@ -149,9 +159,12 @@ type ExtensionServiceServer interface {
 	mustEmbedUnimplementedExtensionServiceServer()
 }
 
-// UnimplementedExtensionServiceServer must be embedded to have forward compatible implementations.
-type UnimplementedExtensionServiceServer struct {
-}
+// UnimplementedExtensionServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedExtensionServiceServer struct{}
 
 func (UnimplementedExtensionServiceServer) Install(context.Context, *InstallRequest) (*common.Operation, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Install not implemented")
@@ -166,6 +179,7 @@ func (UnimplementedExtensionServiceServer) Action(context.Context, *ActionReques
 	return nil, status.Errorf(codes.Unimplemented, "method Action not implemented")
 }
 func (UnimplementedExtensionServiceServer) mustEmbedUnimplementedExtensionServiceServer() {}
+func (UnimplementedExtensionServiceServer) testEmbeddedByValue()                          {}
 
 // 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
@@ -175,6 +189,13 @@ type UnsafeExtensionServiceServer interface {
 }
 
 func RegisterExtensionServiceServer(s grpc.ServiceRegistrar, srv ExtensionServiceServer) {
+	// If the following call pancis, it indicates UnimplementedExtensionServiceServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&ExtensionService_ServiceDesc, srv)
 }
 
diff --git a/proto/extensions/manager_service.pb.go b/proto/extensions/manager_service.pb.go
index 92434b607cb4263c4173979b390c034ce562238e..e0a1160ec1d129af4e10528cfba8635b2dc87569 100644
--- a/proto/extensions/manager_service.pb.go
+++ b/proto/extensions/manager_service.pb.go
@@ -27,8 +27,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: extensions/manager_service.proto
 
 package extensions
@@ -967,7 +967,7 @@ func file_extensions_manager_service_proto_rawDescGZIP() []byte {
 
 var file_extensions_manager_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_extensions_manager_service_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
-var file_extensions_manager_service_proto_goTypes = []interface{}{
+var file_extensions_manager_service_proto_goTypes = []any{
 	(State)(0),                                   // 0: extensions.State
 	(*ListExtensionsRequest)(nil),                // 1: extensions.ListExtensionsRequest
 	(*ListExtensionsResponse)(nil),               // 2: extensions.ListExtensionsResponse
@@ -1012,7 +1012,7 @@ func file_extensions_manager_service_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_extensions_manager_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*ListExtensionsRequest); i {
 			case 0:
 				return &v.state
@@ -1024,7 +1024,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*ListExtensionsResponse); i {
 			case 0:
 				return &v.state
@@ -1036,7 +1036,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*ExtensionDescriptor); i {
 			case 0:
 				return &v.state
@@ -1048,7 +1048,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*RegisterExtensionsRequest); i {
 			case 0:
 				return &v.state
@@ -1060,7 +1060,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*RegisterExtensionsResponse); i {
 			case 0:
 				return &v.state
@@ -1072,7 +1072,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*UnregisterExtensionsRequest); i {
 			case 0:
 				return &v.state
@@ -1084,7 +1084,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*UnregisterExtensionsResponse); i {
 			case 0:
 				return &v.state
@@ -1096,7 +1096,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRegisteredExtensionsRequest); i {
 			case 0:
 				return &v.state
@@ -1108,7 +1108,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRegisteredExtensionsResponse); i {
 			case 0:
 				return &v.state
@@ -1120,7 +1120,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*SpaceExtensions); i {
 			case 0:
 				return &v.state
@@ -1132,7 +1132,7 @@ func file_extensions_manager_service_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_manager_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_extensions_manager_service_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*ListExtensionsResponse_ExtensionInfo); i {
 			case 0:
 				return &v.state
diff --git a/proto/extensions/manager_service_grpc.pb.go b/proto/extensions/manager_service_grpc.pb.go
index c75c7cc3ddcc26817ec7baae4bf59cb5ec37f455..07ee35f5122e15a16f9434657f7b397a7f2d1196 100644
--- a/proto/extensions/manager_service_grpc.pb.go
+++ b/proto/extensions/manager_service_grpc.pb.go
@@ -27,8 +27,8 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: extensions/manager_service.proto
 
 package extensions
@@ -42,8 +42,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	ExtensionManagerService_RegisterExtensions_FullMethodName       = "/extensions.ExtensionManagerService/RegisterExtensions"
@@ -55,6 +55,8 @@ const (
 // 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.
+//
+// ExtensionManager - менеджер расширений. Должен реализовывать так же сервис Extension
 type ExtensionManagerServiceClient interface {
 	// ##  Регистрация расширений
 	//
@@ -85,8 +87,9 @@ func NewExtensionManagerServiceClient(cc grpc.ClientConnInterface) ExtensionMana
 }
 
 func (c *extensionManagerServiceClient) RegisterExtensions(ctx context.Context, in *RegisterExtensionsRequest, opts ...grpc.CallOption) (*RegisterExtensionsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(RegisterExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManagerService_RegisterExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_RegisterExtensions_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -94,8 +97,9 @@ func (c *extensionManagerServiceClient) RegisterExtensions(ctx context.Context,
 }
 
 func (c *extensionManagerServiceClient) UnregisterExtensions(ctx context.Context, in *UnregisterExtensionsRequest, opts ...grpc.CallOption) (*UnregisterExtensionsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(UnregisterExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManagerService_UnregisterExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_UnregisterExtensions_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -103,8 +107,9 @@ func (c *extensionManagerServiceClient) UnregisterExtensions(ctx context.Context
 }
 
 func (c *extensionManagerServiceClient) ListRegisteredExtensions(ctx context.Context, in *ListRegisteredExtensionsRequest, opts ...grpc.CallOption) (*ListRegisteredExtensionsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListRegisteredExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManagerService_ListRegisteredExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_ListRegisteredExtensions_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -112,8 +117,9 @@ func (c *extensionManagerServiceClient) ListRegisteredExtensions(ctx context.Con
 }
 
 func (c *extensionManagerServiceClient) ListExtensions(ctx context.Context, in *ListExtensionsRequest, opts ...grpc.CallOption) (*ListExtensionsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManagerService_ListExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_ListExtensions_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -122,7 +128,9 @@ func (c *extensionManagerServiceClient) ListExtensions(ctx context.Context, in *
 
 // ExtensionManagerServiceServer is the server API for ExtensionManagerService service.
 // All implementations must embed UnimplementedExtensionManagerServiceServer
-// for forward compatibility
+// for forward compatibility.
+//
+// ExtensionManager - менеджер расширений. Должен реализовывать так же сервис Extension
 type ExtensionManagerServiceServer interface {
 	// ##  Регистрация расширений
 	//
@@ -145,9 +153,12 @@ type ExtensionManagerServiceServer interface {
 	mustEmbedUnimplementedExtensionManagerServiceServer()
 }
 
-// UnimplementedExtensionManagerServiceServer must be embedded to have forward compatible implementations.
-type UnimplementedExtensionManagerServiceServer struct {
-}
+// UnimplementedExtensionManagerServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedExtensionManagerServiceServer struct{}
 
 func (UnimplementedExtensionManagerServiceServer) RegisterExtensions(context.Context, *RegisterExtensionsRequest) (*RegisterExtensionsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RegisterExtensions not implemented")
@@ -163,6 +174,7 @@ func (UnimplementedExtensionManagerServiceServer) ListExtensions(context.Context
 }
 func (UnimplementedExtensionManagerServiceServer) mustEmbedUnimplementedExtensionManagerServiceServer() {
 }
+func (UnimplementedExtensionManagerServiceServer) testEmbeddedByValue() {}
 
 // 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
@@ -172,6 +184,13 @@ type UnsafeExtensionManagerServiceServer interface {
 }
 
 func RegisterExtensionManagerServiceServer(s grpc.ServiceRegistrar, srv ExtensionManagerServiceServer) {
+	// If the following call pancis, it indicates UnimplementedExtensionManagerServiceServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&ExtensionManagerService_ServiceDesc, srv)
 }
 
diff --git a/proto/files/files.pb.go b/proto/files/files.pb.go
index 8f93164f9b2f1957fd62aca0fe29a7cdc62d4b29..655ebff92f49c17fddd05bb9cfa8a8cf988a2fa2 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: files/files.proto
 
 package files
@@ -28,7 +28,7 @@ type File struct {
 
 	Id       string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`                             // Уникальный идентификатор файла в хранилище
 	Name     string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`                         // Имя файла
-	Size     int32  `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`                        // Размер файла
+	Size     uint64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`                        // Размер файла
 	MimeType string `protobuf:"bytes,4,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"` // Mime-type файла
 	Url      string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"`                           // Адрес для загрузки файла
 }
@@ -79,7 +79,7 @@ func (x *File) GetName() string {
 	return ""
 }
 
-func (x *File) GetSize() int32 {
+func (x *File) GetSize() uint64 {
 	if x != nil {
 		return x.Size
 	}
@@ -107,7 +107,7 @@ type MultipartUpload struct {
 
 	File     *File            `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"`
 	UploadId string           `protobuf:"bytes,2,opt,name=upload_id,json=uploadId,proto3" json:"upload_id,omitempty"`  // Идентификатор загрузки хранилища
-	PartSize int32            `protobuf:"varint,3,opt,name=part_size,json=partSize,proto3" json:"part_size,omitempty"` // Размер блока для загрузки
+	PartSize uint64           `protobuf:"varint,3,opt,name=part_size,json=partSize,proto3" json:"part_size,omitempty"` // Размер блока для загрузки
 	PartUrls []string         `protobuf:"bytes,4,rep,name=part_urls,json=partUrls,proto3" json:"part_urls,omitempty"`  // Адреса для загрузки пол
 	Parts    []*CompletedPart `protobuf:"bytes,5,rep,name=parts,proto3" json:"parts,omitempty"`                        // Идентификаторы загруженных блоков (S3 ETAGs)
 }
@@ -158,7 +158,7 @@ func (x *MultipartUpload) GetUploadId() string {
 	return ""
 }
 
-func (x *MultipartUpload) GetPartSize() int32 {
+func (x *MultipartUpload) GetPartSize() uint64 {
 	if x != nil {
 		return x.PartSize
 	}
@@ -939,7 +939,7 @@ var file_files_files_proto_rawDesc = []byte{
 	0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
 	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
 	0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x05, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f,
+	0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f,
 	0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65,
 	0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xb5, 0x01, 0x0a, 0x0f, 0x4d, 0x75, 0x6c, 0x74, 0x69,
@@ -948,7 +948,7 @@ var file_files_files_proto_rawDesc = []byte{
 	0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x75,
 	0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
 	0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74,
-	0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x72,
+	0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x70, 0x61, 0x72,
 	0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74, 0x5f, 0x75, 0x72,
 	0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x74, 0x55, 0x72,
 	0x6c, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
@@ -1060,7 +1060,7 @@ func file_files_files_proto_rawDescGZIP() []byte {
 }
 
 var file_files_files_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
-var file_files_files_proto_goTypes = []interface{}{
+var file_files_files_proto_goTypes = []any{
 	(*File)(nil),                   // 0: files.File
 	(*MultipartUpload)(nil),        // 1: files.MultipartUpload
 	(*Upload)(nil),                 // 2: files.Upload
@@ -1123,7 +1123,7 @@ func file_files_files_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_files_files_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*File); i {
 			case 0:
 				return &v.state
@@ -1135,7 +1135,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*MultipartUpload); i {
 			case 0:
 				return &v.state
@@ -1147,7 +1147,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*Upload); i {
 			case 0:
 				return &v.state
@@ -1159,7 +1159,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*CompletedPart); i {
 			case 0:
 				return &v.state
@@ -1171,7 +1171,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*StartUploadRequest); i {
 			case 0:
 				return &v.state
@@ -1183,7 +1183,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*StartUploadResponse); i {
 			case 0:
 				return &v.state
@@ -1195,7 +1195,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*CompleteUploadRequest); i {
 			case 0:
 				return &v.state
@@ -1207,7 +1207,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*CompleteUploadResponse); i {
 			case 0:
 				return &v.state
@@ -1219,7 +1219,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*AbortUploadRequest); i {
 			case 0:
 				return &v.state
@@ -1231,7 +1231,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*AbortUploadResponse); i {
 			case 0:
 				return &v.state
@@ -1243,7 +1243,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*MoveUploadRequest); i {
 			case 0:
 				return &v.state
@@ -1255,7 +1255,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*MoveUploadResponse); i {
 			case 0:
 				return &v.state
@@ -1267,7 +1267,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*UploadRequest); i {
 			case 0:
 				return &v.state
@@ -1279,7 +1279,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*UploadResponse); i {
 			case 0:
 				return &v.state
@@ -1291,7 +1291,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*GetFileRequest); i {
 			case 0:
 				return &v.state
@@ -1303,7 +1303,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[15].Exporter = func(v any, i int) any {
 			switch v := v.(*GetFileResponse); i {
 			case 0:
 				return &v.state
@@ -1315,7 +1315,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[16].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteFileRequest); i {
 			case 0:
 				return &v.state
@@ -1327,7 +1327,7 @@ func file_files_files_proto_init() {
 				return nil
 			}
 		}
-		file_files_files_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
+		file_files_files_proto_msgTypes[17].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteFileResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/files/files_grpc.pb.go b/proto/files/files_grpc.pb.go
index 234e28599a99f74e704121aeda9e82a9f37005b4..ae5d8c4766e129958ebe093cb636f90bc1756426 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: files/files.proto
 
 package files
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Files_StartUpload_FullMethodName    = "/files.Files/StartUpload"
@@ -71,8 +71,9 @@ func NewFilesClient(cc grpc.ClientConnInterface) FilesClient {
 }
 
 func (c *filesClient) StartUpload(ctx context.Context, in *StartUploadRequest, opts ...grpc.CallOption) (*StartUploadResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(StartUploadResponse)
-	err := c.cc.Invoke(ctx, Files_StartUpload_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_StartUpload_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -80,8 +81,9 @@ func (c *filesClient) StartUpload(ctx context.Context, in *StartUploadRequest, o
 }
 
 func (c *filesClient) CompleteUpload(ctx context.Context, in *CompleteUploadRequest, opts ...grpc.CallOption) (*CompleteUploadResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CompleteUploadResponse)
-	err := c.cc.Invoke(ctx, Files_CompleteUpload_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_CompleteUpload_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -89,8 +91,9 @@ func (c *filesClient) CompleteUpload(ctx context.Context, in *CompleteUploadRequ
 }
 
 func (c *filesClient) AbortUpload(ctx context.Context, in *AbortUploadRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Files_AbortUpload_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_AbortUpload_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -98,8 +101,9 @@ func (c *filesClient) AbortUpload(ctx context.Context, in *AbortUploadRequest, o
 }
 
 func (c *filesClient) MoveUpload(ctx context.Context, in *MoveUploadRequest, opts ...grpc.CallOption) (*MoveUploadResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(MoveUploadResponse)
-	err := c.cc.Invoke(ctx, Files_MoveUpload_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_MoveUpload_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -107,8 +111,9 @@ func (c *filesClient) MoveUpload(ctx context.Context, in *MoveUploadRequest, opt
 }
 
 func (c *filesClient) Upload(ctx context.Context, in *UploadRequest, opts ...grpc.CallOption) (*UploadResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(UploadResponse)
-	err := c.cc.Invoke(ctx, Files_Upload_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_Upload_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -116,8 +121,9 @@ func (c *filesClient) Upload(ctx context.Context, in *UploadRequest, opts ...grp
 }
 
 func (c *filesClient) GetFile(ctx context.Context, in *GetFileRequest, opts ...grpc.CallOption) (*GetFileResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetFileResponse)
-	err := c.cc.Invoke(ctx, Files_GetFile_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_GetFile_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -125,8 +131,9 @@ func (c *filesClient) GetFile(ctx context.Context, in *GetFileRequest, opts ...g
 }
 
 func (c *filesClient) DeleteFile(ctx context.Context, in *DeleteFileRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Files_DeleteFile_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Files_DeleteFile_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -135,7 +142,7 @@ func (c *filesClient) DeleteFile(ctx context.Context, in *DeleteFileRequest, opt
 
 // FilesServer is the server API for Files service.
 // All implementations must embed UnimplementedFilesServer
-// for forward compatibility
+// for forward compatibility.
 type FilesServer interface {
 	// StartUpload - инициирует процедуру загрузки файла в файловое хранилище.
 	// Используется клиентским приложением для начала загрузки файла
@@ -167,9 +174,12 @@ type FilesServer interface {
 	mustEmbedUnimplementedFilesServer()
 }
 
-// UnimplementedFilesServer must be embedded to have forward compatible implementations.
-type UnimplementedFilesServer struct {
-}
+// UnimplementedFilesServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedFilesServer struct{}
 
 func (UnimplementedFilesServer) StartUpload(context.Context, *StartUploadRequest) (*StartUploadResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method StartUpload not implemented")
@@ -193,6 +203,7 @@ func (UnimplementedFilesServer) DeleteFile(context.Context, *DeleteFileRequest)
 	return nil, status.Errorf(codes.Unimplemented, "method DeleteFile not implemented")
 }
 func (UnimplementedFilesServer) mustEmbedUnimplementedFilesServer() {}
+func (UnimplementedFilesServer) testEmbeddedByValue()               {}
 
 // UnsafeFilesServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to FilesServer will
@@ -202,6 +213,13 @@ type UnsafeFilesServer interface {
 }
 
 func RegisterFilesServer(s grpc.ServiceRegistrar, srv FilesServer) {
+	// If the following call pancis, it indicates UnimplementedFilesServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Files_ServiceDesc, srv)
 }
 
diff --git a/proto/images/images.pb.go b/proto/images/images.pb.go
index 4f95cb6da087725f8af05fbca35955d9e02c7608..e84dae61c1ff2493731ccfbcaec30046b6c7b6fc 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: images/images.proto
 
 package images
@@ -270,7 +270,7 @@ func file_images_images_proto_rawDescGZIP() []byte {
 }
 
 var file_images_images_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
-var file_images_images_proto_goTypes = []interface{}{
+var file_images_images_proto_goTypes = []any{
 	(*Param)(nil),                 // 0: images.Param
 	(*GetRequest)(nil),            // 1: images.GetRequest
 	(*GetResponse)(nil),           // 2: images.GetResponse
@@ -297,7 +297,7 @@ func file_images_images_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_images_images_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_images_images_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Param); i {
 			case 0:
 				return &v.state
@@ -309,7 +309,7 @@ func file_images_images_proto_init() {
 				return nil
 			}
 		}
-		file_images_images_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_images_images_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -321,7 +321,7 @@ func file_images_images_proto_init() {
 				return nil
 			}
 		}
-		file_images_images_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_images_images_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -333,7 +333,7 @@ func file_images_images_proto_init() {
 				return nil
 			}
 		}
-		file_images_images_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_images_images_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest_GetOptions); i {
 			case 0:
 				return &v.state
diff --git a/proto/images/images_grpc.pb.go b/proto/images/images_grpc.pb.go
index 272c6580743f491a78834708bdcacfef2a166f86..669dcc8e9a29cefefb38ade60d4d48900d268707 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: images/images.proto
 
 package images
@@ -15,8 +15,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Images_Get_FullMethodName = "/images.Images/Get"
@@ -25,6 +25,8 @@ const (
 // ImagesClient is the client API for Images 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.
+//
+// ImageService - сервис для обработки изображений
 type ImagesClient interface {
 	// Get - к файлу, идентификатор которого передан в запросе, применяются параметры.
 	// Может быть передано несколько параметров, порядок учитывается при обработке
@@ -47,8 +49,9 @@ func NewImagesClient(cc grpc.ClientConnInterface) ImagesClient {
 }
 
 func (c *imagesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Images_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Images_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -57,7 +60,9 @@ func (c *imagesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Cal
 
 // ImagesServer is the server API for Images service.
 // All implementations must embed UnimplementedImagesServer
-// for forward compatibility
+// for forward compatibility.
+//
+// ImageService - сервис для обработки изображений
 type ImagesServer interface {
 	// Get - к файлу, идентификатор которого передан в запросе, применяются параметры.
 	// Может быть передано несколько параметров, порядок учитывается при обработке
@@ -72,14 +77,18 @@ type ImagesServer interface {
 	mustEmbedUnimplementedImagesServer()
 }
 
-// UnimplementedImagesServer must be embedded to have forward compatible implementations.
-type UnimplementedImagesServer struct {
-}
+// UnimplementedImagesServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedImagesServer struct{}
 
 func (UnimplementedImagesServer) Get(context.Context, *GetRequest) (*GetResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
 }
 func (UnimplementedImagesServer) mustEmbedUnimplementedImagesServer() {}
+func (UnimplementedImagesServer) testEmbeddedByValue()                {}
 
 // UnsafeImagesServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to ImagesServer will
@@ -89,6 +98,13 @@ type UnsafeImagesServer interface {
 }
 
 func RegisterImagesServer(s grpc.ServiceRegistrar, srv ImagesServer) {
+	// If the following call pancis, it indicates UnimplementedImagesServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Images_ServiceDesc, srv)
 }
 
diff --git a/proto/invitations/invitations.pb.go b/proto/invitations/invitations.pb.go
index 1e9fd729c0ffba1903d60f70ba8f89041684b11d..5fd81d66ef5f3b27a7d8c4de428cca03e6d53de1 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: invitations/invitations.proto
 
 package invitations
@@ -1021,7 +1021,7 @@ func file_invitations_invitations_proto_rawDescGZIP() []byte {
 }
 
 var file_invitations_invitations_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
-var file_invitations_invitations_proto_goTypes = []interface{}{
+var file_invitations_invitations_proto_goTypes = []any{
 	(*Invitation)(nil),                    // 0: content.invitations.Invitation
 	(*Filter)(nil),                        // 1: content.invitations.Filter
 	(*FindOptions)(nil),                   // 2: content.invitations.FindOptions
@@ -1077,7 +1077,7 @@ func file_invitations_invitations_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_invitations_invitations_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Invitation); i {
 			case 0:
 				return &v.state
@@ -1089,7 +1089,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -1101,7 +1101,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*FindOptions); i {
 			case 0:
 				return &v.state
@@ -1113,7 +1113,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -1125,7 +1125,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -1137,7 +1137,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -1149,7 +1149,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -1161,7 +1161,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -1173,7 +1173,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateInvitationResponse); i {
 			case 0:
 				return &v.state
@@ -1185,7 +1185,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*AcceptRequest); i {
 			case 0:
 				return &v.state
@@ -1197,7 +1197,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*AcceptInvitationResponse); i {
 			case 0:
 				return &v.state
@@ -1209,7 +1209,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*FindRequest); i {
 			case 0:
 				return &v.state
@@ -1221,7 +1221,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*FindResponse); i {
 			case 0:
 				return &v.state
@@ -1233,7 +1233,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -1245,7 +1245,7 @@ func file_invitations_invitations_proto_init() {
 				return nil
 			}
 		}
-		file_invitations_invitations_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_invitations_invitations_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteSpaceInvitationResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/invitations/invitations_grpc.pb.go b/proto/invitations/invitations_grpc.pb.go
index d5137fec4deee51fb509d7ee0ba9e0c235f57668..767261cba6341aea650345743e27fce8804d3d99 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: invitations/invitations.proto
 
 package invitations
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Invitations_Create_FullMethodName = "/content.invitations.Invitations/Create"
@@ -51,8 +51,9 @@ func NewInvitationsClient(cc grpc.ClientConnInterface) InvitationsClient {
 }
 
 func (c *invitationsClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Invitations_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Invitations_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -60,8 +61,9 @@ func (c *invitationsClient) Create(ctx context.Context, in *CreateRequest, opts
 }
 
 func (c *invitationsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Invitations_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Invitations_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -69,8 +71,9 @@ func (c *invitationsClient) Get(ctx context.Context, in *GetRequest, opts ...grp
 }
 
 func (c *invitationsClient) Accept(ctx context.Context, in *AcceptRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Invitations_Accept_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Invitations_Accept_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -78,8 +81,9 @@ func (c *invitationsClient) Accept(ctx context.Context, in *AcceptRequest, opts
 }
 
 func (c *invitationsClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindResponse)
-	err := c.cc.Invoke(ctx, Invitations_Find_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Invitations_Find_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -87,8 +91,9 @@ func (c *invitationsClient) Find(ctx context.Context, in *FindRequest, opts ...g
 }
 
 func (c *invitationsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Invitations_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Invitations_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -97,7 +102,7 @@ func (c *invitationsClient) Delete(ctx context.Context, in *DeleteRequest, opts
 
 // InvitationsServer is the server API for Invitations service.
 // All implementations must embed UnimplementedInvitationsServer
-// for forward compatibility
+// for forward compatibility.
 type InvitationsServer interface {
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
 	// Получить данные о приглашении
@@ -111,9 +116,12 @@ type InvitationsServer interface {
 	mustEmbedUnimplementedInvitationsServer()
 }
 
-// UnimplementedInvitationsServer must be embedded to have forward compatible implementations.
-type UnimplementedInvitationsServer struct {
-}
+// UnimplementedInvitationsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedInvitationsServer struct{}
 
 func (UnimplementedInvitationsServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -131,6 +139,7 @@ func (UnimplementedInvitationsServer) Delete(context.Context, *DeleteRequest) (*
 	return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
 }
 func (UnimplementedInvitationsServer) mustEmbedUnimplementedInvitationsServer() {}
+func (UnimplementedInvitationsServer) testEmbeddedByValue()                     {}
 
 // UnsafeInvitationsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to InvitationsServer will
@@ -140,6 +149,13 @@ type UnsafeInvitationsServer interface {
 }
 
 func RegisterInvitationsServer(s grpc.ServiceRegistrar, srv InvitationsServer) {
+	// If the following call pancis, it indicates UnimplementedInvitationsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Invitations_ServiceDesc, srv)
 }
 
diff --git a/proto/items/items.pb.go b/proto/items/items.pb.go
index 7843fcdbb318fbf0540eb789b3fe50ed6e687a0c..a49876ec9c67d5a76a29330cff5dfea32c51c0aa 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: items/items.proto
 
 package items
@@ -366,25 +366,42 @@ type Item struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Id                  string                      `protobuf:"bytes,1,opt,name=id,proto3" json:"id,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"`
-	CollectionId        string                      `protobuf:"bytes,4,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
-	State               Item_State                  `protobuf:"varint,5,opt,name=state,proto3,enum=content.items.Item_State" json:"state,omitempty"`
-	CreatedRevAt        *timestamppb.Timestamp      `protobuf:"bytes,6,opt,name=created_rev_at,json=createdRevAt,proto3" json:"created_rev_at,omitempty"` // дата создания текущей ревизии
-	CreatedBy           string                      `protobuf:"bytes,7,opt,name=created_by,json=createdBy,proto3" json:"created_by,omitempty"`            // id пользователя создавшего первую ревизию
-	CreatedAt           *timestamppb.Timestamp      `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`            // дата создания первой ревизии
-	UpdatedBy           string                      `protobuf:"bytes,9,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"`            // id пользователя обновившего текущую ревизию
-	UpdatedAt           *timestamppb.Timestamp      `protobuf:"bytes,10,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`           // дата обновления текущей ревизии
-	Data                *structpb.Struct            `protobuf:"bytes,11,opt,name=data,proto3" json:"data,omitempty"`
-	Translations        map[string]*structpb.Struct `protobuf:"bytes,12,rep,name=translations,proto3" json:"translations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
-	RevisionId          string                      `protobuf:"bytes,13,opt,name=revision_id,json=revisionId,proto3" json:"revision_id,omitempty"`
-	RevisionDescription string                      `protobuf:"bytes,14,opt,name=revision_description,json=revisionDescription,proto3" json:"revision_description,omitempty"`
-	Locale              string                      `protobuf:"bytes,18,opt,name=locale,proto3" json:"locale,omitempty"`
-	Deleted             bool                        `protobuf:"varint,19,opt,name=deleted,proto3" json:"deleted,omitempty"`
-	Hidden              bool                        `protobuf:"varint,20,opt,name=hidden,proto3" json:"hidden,omitempty"`
-	Template            bool                        `protobuf:"varint,21,opt,name=template,proto3" json:"template,omitempty"`
-	Permissions         *Permissions                `protobuf:"bytes,22,opt,name=permissions,proto3" json:"permissions,omitempty"`
+	Id                  string                 `protobuf:"bytes,1,opt,name=id,proto3" json:"id,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"`
+	CollectionId        string                 `protobuf:"bytes,4,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
+	State               Item_State             `protobuf:"varint,5,opt,name=state,proto3,enum=content.items.Item_State" json:"state,omitempty"`
+	CreatedRevAt        *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_rev_at,json=createdRevAt,proto3" json:"created_rev_at,omitempty"` // дата создания текущей ревизии
+	CreatedBy           string                 `protobuf:"bytes,7,opt,name=created_by,json=createdBy,proto3" json:"created_by,omitempty"`            // id пользователя создавшего первую ревизию
+	CreatedAt           *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`            // дата создания первой ревизии
+	UpdatedBy           string                 `protobuf:"bytes,9,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"`            // id пользователя обновившего текущую ревизию
+	UpdatedAt           *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`           // дата обновления текущей ревизии
+	Data                *structpb.Struct       `protobuf:"bytes,11,opt,name=data,proto3" json:"data,omitempty"`
+	RevisionId          string                 `protobuf:"bytes,13,opt,name=revision_id,json=revisionId,proto3" json:"revision_id,omitempty"`
+	RevisionDescription string                 `protobuf:"bytes,14,opt,name=revision_description,json=revisionDescription,proto3" json:"revision_description,omitempty"`
+	Deleted             bool                   `protobuf:"varint,19,opt,name=deleted,proto3" json:"deleted,omitempty"`
+	Hidden              bool                   `protobuf:"varint,20,opt,name=hidden,proto3" json:"hidden,omitempty"`
+	Template            bool                   `protobuf:"varint,21,opt,name=template,proto3" json:"template,omitempty"`
+	Permissions         *Permissions           `protobuf:"bytes,22,opt,name=permissions,proto3" json:"permissions,omitempty"`
+	SearchScore         float64                `protobuf:"fixed64,23,opt,name=search_score,json=searchScore,proto3" json:"search_score,omitempty"` // релеватность элемента при полнотекстовом поиске
+	// Идентификатор локали полученной записи.
+	// При создании или обновлении идентификатор локали, в которой создается запись, опционально.
+	// Если указан, то создается перевод для указанного языка, поле translations игнорируется
+	LocaleId string `protobuf:"bytes,100,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`
+	// Позволяет одновременно установить/получить несколько переводов и производить манипуляции с переводами
+	// Ключами является идентификатор локали, значениями - данные переводы
+	// При обновлении не происходит валидация или модификация каждого из переводов в соответствие со схемой,
+	// поэтому обновление через поле `translations` стоит выполнять с аккуратностью
+	// Для удаления переводов реализована следующая логика:
+	// - {"lang":nil|{}} - сброс перевода для языка
+	// - {"lang":map{...}} - установка перевода для языка
+	// - {"lang":map{...}, "*":nil} - установка перевода для языка, сброс остальных переводов
+	// - {"*":nil} - сброс всех переводов
+	Translations map[string]*structpb.Struct `protobuf:"bytes,12,rep,name=translations,proto3" json:"translations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	// Список идентификаторов локалей, для которых есть переводы
+	// Соотвествует списку переводов в translations, при получении записи всегда возвращается
+	// полный список. Невозможно обновить вручную: формируется системой
+	TranslationsIds []string `protobuf:"bytes,101,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"`
 }
 
 func (x *Item) Reset() {
@@ -496,13 +513,6 @@ func (x *Item) GetData() *structpb.Struct {
 	return nil
 }
 
-func (x *Item) GetTranslations() map[string]*structpb.Struct {
-	if x != nil {
-		return x.Translations
-	}
-	return nil
-}
-
 func (x *Item) GetRevisionId() string {
 	if x != nil {
 		return x.RevisionId
@@ -517,13 +527,6 @@ func (x *Item) GetRevisionDescription() string {
 	return ""
 }
 
-func (x *Item) GetLocale() string {
-	if x != nil {
-		return x.Locale
-	}
-	return ""
-}
-
 func (x *Item) GetDeleted() bool {
 	if x != nil {
 		return x.Deleted
@@ -552,6 +555,34 @@ func (x *Item) GetPermissions() *Permissions {
 	return nil
 }
 
+func (x *Item) GetSearchScore() float64 {
+	if x != nil {
+		return x.SearchScore
+	}
+	return 0
+}
+
+func (x *Item) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *Item) GetTranslations() map[string]*structpb.Struct {
+	if x != nil {
+		return x.Translations
+	}
+	return nil
+}
+
+func (x *Item) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 type EventCreate struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1027,6 +1058,10 @@ type FindOptions struct {
 	Regular   bool                `protobuf:"varint,4,opt,name=regular,proto3" json:"regular,omitempty"`
 	Hidden    bool                `protobuf:"varint,5,opt,name=hidden,proto3" json:"hidden,omitempty"`
 	Templates bool                `protobuf:"varint,6,opt,name=templates,proto3" json:"templates,omitempty"`
+	LocaleId  string              `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"` // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	// Возможно указание '*' для получения всех переводов
+	TranslationsIds []string `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"`
 }
 
 func (x *FindOptions) Reset() {
@@ -1096,6 +1131,20 @@ func (x *FindOptions) GetTemplates() bool {
 	return false
 }
 
+func (x *FindOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *FindOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 type UpdateOptions struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1148,7 +1197,10 @@ type GetPublishedOptions struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	LocaleId string `protobuf:"bytes,1,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`
+	LocaleId string `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"` // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	// Возможно указание '*' для получения всех переводов
+	TranslationsIds []string `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"`
 }
 
 func (x *GetPublishedOptions) Reset() {
@@ -1190,6 +1242,13 @@ func (x *GetPublishedOptions) GetLocaleId() string {
 	return ""
 }
 
+func (x *GetPublishedOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 type DeleteOptions struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1391,11 +1450,15 @@ type FindPublishedOptions struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Options   *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
-	LocaleId  string              `protobuf:"bytes,3,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`
-	Regular   bool                `protobuf:"varint,4,opt,name=regular,proto3" json:"regular,omitempty"`
-	Hidden    bool                `protobuf:"varint,5,opt,name=hidden,proto3" json:"hidden,omitempty"`
-	Templates bool                `protobuf:"varint,6,opt,name=templates,proto3" json:"templates,omitempty"`
+	Options *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+	// string locale_id = 3; // язык для поиска переводов. Если не указан, то возвращаются данные для языка по умолчанию
+	Regular   bool   `protobuf:"varint,4,opt,name=regular,proto3" json:"regular,omitempty"`
+	Hidden    bool   `protobuf:"varint,5,opt,name=hidden,proto3" json:"hidden,omitempty"`
+	Templates bool   `protobuf:"varint,6,opt,name=templates,proto3" json:"templates,omitempty"`
+	LocaleId  string `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"` // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	// Список идентификаторов переводов/локалей, которых должны быть включены в результат
+	// Возможно указание '*' для получения всех переводов
+	TranslationsIds []string `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"`
 }
 
 func (x *FindPublishedOptions) Reset() {
@@ -1437,13 +1500,6 @@ func (x *FindPublishedOptions) GetOptions() *common.FindOptions {
 	return nil
 }
 
-func (x *FindPublishedOptions) GetLocaleId() string {
-	if x != nil {
-		return x.LocaleId
-	}
-	return ""
-}
-
 func (x *FindPublishedOptions) GetRegular() bool {
 	if x != nil {
 		return x.Regular
@@ -1465,12 +1521,28 @@ func (x *FindPublishedOptions) GetTemplates() bool {
 	return false
 }
 
+func (x *FindPublishedOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *FindPublishedOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 type FindArchivedOptions struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Options *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+	Options         *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+	LocaleId        string              `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`                      // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	TranslationsIds []string            `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"` // Список идентификаторов переводов/локалей, которых должны быть включены в результат
 }
 
 func (x *FindArchivedOptions) Reset() {
@@ -1512,12 +1584,28 @@ func (x *FindArchivedOptions) GetOptions() *common.FindOptions {
 	return nil
 }
 
+func (x *FindArchivedOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *FindArchivedOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 type ListRevisionsOptions struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Options *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+	Options         *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+	LocaleId        string              `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`                      // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	TranslationsIds []string            `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"` // Список идентификаторов переводов/локалей, которых должны быть включены в результат
 }
 
 func (x *ListRevisionsOptions) Reset() {
@@ -1559,6 +1647,75 @@ func (x *ListRevisionsOptions) GetOptions() *common.FindOptions {
 	return nil
 }
 
+func (x *ListRevisionsOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *ListRevisionsOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
+type GetRevisionOptions struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	LocaleId        string   `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`                      // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	TranslationsIds []string `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"` // Список идентификаторов переводов/локалей, которых должны быть включены в результат
+}
+
+func (x *GetRevisionOptions) Reset() {
+	*x = GetRevisionOptions{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_items_items_proto_msgTypes[23]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetRevisionOptions) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetRevisionOptions) ProtoMessage() {}
+
+func (x *GetRevisionOptions) ProtoReflect() protoreflect.Message {
+	mi := &file_items_items_proto_msgTypes[23]
+	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 GetRevisionOptions.ProtoReflect.Descriptor instead.
+func (*GetRevisionOptions) Descriptor() ([]byte, []int) {
+	return file_items_items_proto_rawDescGZIP(), []int{23}
+}
+
+func (x *GetRevisionOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *GetRevisionOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 // Fields - поля которые должны быть возвращены или вычислены в результате.
 // Ключ (string) - имя поля под которым будет добавляться результат.
 // Значение (string) - является выражением, вычисление которого сформирует результат
@@ -1581,7 +1738,7 @@ type AggregateOptions struct {
 func (x *AggregateOptions) Reset() {
 	*x = AggregateOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[23]
+		mi := &file_items_items_proto_msgTypes[24]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1594,7 +1751,7 @@ func (x *AggregateOptions) String() string {
 func (*AggregateOptions) ProtoMessage() {}
 
 func (x *AggregateOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[23]
+	mi := &file_items_items_proto_msgTypes[24]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1607,7 +1764,7 @@ func (x *AggregateOptions) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AggregateOptions.ProtoReflect.Descriptor instead.
 func (*AggregateOptions) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{23}
+	return file_items_items_proto_rawDescGZIP(), []int{24}
 }
 
 func (x *AggregateOptions) GetFields() map[string]string {
@@ -1628,7 +1785,7 @@ type AggregatePublishedOptions struct {
 func (x *AggregatePublishedOptions) Reset() {
 	*x = AggregatePublishedOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[24]
+		mi := &file_items_items_proto_msgTypes[25]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1641,7 +1798,7 @@ func (x *AggregatePublishedOptions) String() string {
 func (*AggregatePublishedOptions) ProtoMessage() {}
 
 func (x *AggregatePublishedOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[24]
+	mi := &file_items_items_proto_msgTypes[25]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1654,7 +1811,7 @@ func (x *AggregatePublishedOptions) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AggregatePublishedOptions.ProtoReflect.Descriptor instead.
 func (*AggregatePublishedOptions) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{24}
+	return file_items_items_proto_rawDescGZIP(), []int{25}
 }
 
 func (x *AggregatePublishedOptions) GetFields() map[string]string {
@@ -1676,7 +1833,7 @@ type CreateRequest struct {
 func (x *CreateRequest) Reset() {
 	*x = CreateRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[25]
+		mi := &file_items_items_proto_msgTypes[26]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1689,7 +1846,7 @@ func (x *CreateRequest) String() string {
 func (*CreateRequest) ProtoMessage() {}
 
 func (x *CreateRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[25]
+	mi := &file_items_items_proto_msgTypes[26]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1702,7 +1859,7 @@ func (x *CreateRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateRequest.ProtoReflect.Descriptor instead.
 func (*CreateRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{25}
+	return file_items_items_proto_rawDescGZIP(), []int{26}
 }
 
 func (x *CreateRequest) GetItem() *Item {
@@ -1730,7 +1887,7 @@ type CreateResponse struct {
 func (x *CreateResponse) Reset() {
 	*x = CreateResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[26]
+		mi := &file_items_items_proto_msgTypes[27]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1743,7 +1900,7 @@ func (x *CreateResponse) String() string {
 func (*CreateResponse) ProtoMessage() {}
 
 func (x *CreateResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[26]
+	mi := &file_items_items_proto_msgTypes[27]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1756,7 +1913,7 @@ func (x *CreateResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateResponse.ProtoReflect.Descriptor instead.
 func (*CreateResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{26}
+	return file_items_items_proto_rawDescGZIP(), []int{27}
 }
 
 func (x *CreateResponse) GetCreated() *Item {
@@ -1777,7 +1934,7 @@ type IntrospectRequest struct {
 func (x *IntrospectRequest) Reset() {
 	*x = IntrospectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[27]
+		mi := &file_items_items_proto_msgTypes[28]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1790,7 +1947,7 @@ func (x *IntrospectRequest) String() string {
 func (*IntrospectRequest) ProtoMessage() {}
 
 func (x *IntrospectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[27]
+	mi := &file_items_items_proto_msgTypes[28]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1803,7 +1960,7 @@ func (x *IntrospectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use IntrospectRequest.ProtoReflect.Descriptor instead.
 func (*IntrospectRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{27}
+	return file_items_items_proto_rawDescGZIP(), []int{28}
 }
 
 func (x *IntrospectRequest) GetItem() *Item {
@@ -1826,7 +1983,7 @@ type IntrospectResponse struct {
 func (x *IntrospectResponse) Reset() {
 	*x = IntrospectResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[28]
+		mi := &file_items_items_proto_msgTypes[29]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1839,7 +1996,7 @@ func (x *IntrospectResponse) String() string {
 func (*IntrospectResponse) ProtoMessage() {}
 
 func (x *IntrospectResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[28]
+	mi := &file_items_items_proto_msgTypes[29]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1852,7 +2009,7 @@ func (x *IntrospectResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use IntrospectResponse.ProtoReflect.Descriptor instead.
 func (*IntrospectResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{28}
+	return file_items_items_proto_rawDescGZIP(), []int{29}
 }
 
 func (x *IntrospectResponse) GetItem() *Item {
@@ -1876,21 +2033,77 @@ func (x *IntrospectResponse) GetValidationErrors() []*common.Error_BadRequest_Fi
 	return nil
 }
 
+type GetOptions struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	LocaleId        string   `protobuf:"bytes,7,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`                      // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	TranslationsIds []string `protobuf:"bytes,8,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"` // Список идентификаторов переводов/локалей, которых должны быть включены в результат
+}
+
+func (x *GetOptions) Reset() {
+	*x = GetOptions{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_items_items_proto_msgTypes[30]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetOptions) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetOptions) ProtoMessage() {}
+
+func (x *GetOptions) ProtoReflect() protoreflect.Message {
+	mi := &file_items_items_proto_msgTypes[30]
+	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 GetOptions.ProtoReflect.Descriptor instead.
+func (*GetOptions) Descriptor() ([]byte, []int) {
+	return file_items_items_proto_rawDescGZIP(), []int{30}
+}
+
+func (x *GetOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *GetOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 type GetRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	SpaceId      string `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
-	EnvId        string `protobuf:"bytes,2,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
-	CollectionId string `protobuf:"bytes,3,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
-	ItemId       string `protobuf:"bytes,4,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"`
+	SpaceId      string      `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
+	EnvId        string      `protobuf:"bytes,2,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
+	CollectionId string      `protobuf:"bytes,3,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
+	ItemId       string      `protobuf:"bytes,4,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"`
+	Options      *GetOptions `protobuf:"bytes,5,opt,name=options,proto3" json:"options,omitempty"`
 }
 
 func (x *GetRequest) Reset() {
 	*x = GetRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[29]
+		mi := &file_items_items_proto_msgTypes[31]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1903,7 +2116,7 @@ func (x *GetRequest) String() string {
 func (*GetRequest) ProtoMessage() {}
 
 func (x *GetRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[29]
+	mi := &file_items_items_proto_msgTypes[31]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1916,7 +2129,7 @@ func (x *GetRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetRequest.ProtoReflect.Descriptor instead.
 func (*GetRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{29}
+	return file_items_items_proto_rawDescGZIP(), []int{31}
 }
 
 func (x *GetRequest) GetSpaceId() string {
@@ -1947,6 +2160,13 @@ func (x *GetRequest) GetItemId() string {
 	return ""
 }
 
+func (x *GetRequest) GetOptions() *GetOptions {
+	if x != nil {
+		return x.Options
+	}
+	return nil
+}
+
 type GetResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1958,7 +2178,7 @@ type GetResponse struct {
 func (x *GetResponse) Reset() {
 	*x = GetResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[30]
+		mi := &file_items_items_proto_msgTypes[32]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1971,7 +2191,7 @@ func (x *GetResponse) String() string {
 func (*GetResponse) ProtoMessage() {}
 
 func (x *GetResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[30]
+	mi := &file_items_items_proto_msgTypes[32]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1984,7 +2204,7 @@ func (x *GetResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetResponse.ProtoReflect.Descriptor instead.
 func (*GetResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{30}
+	return file_items_items_proto_rawDescGZIP(), []int{32}
 }
 
 func (x *GetResponse) GetItem() *Item {
@@ -2009,7 +2229,7 @@ type FindRequest struct {
 func (x *FindRequest) Reset() {
 	*x = FindRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[31]
+		mi := &file_items_items_proto_msgTypes[33]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2022,7 +2242,7 @@ func (x *FindRequest) String() string {
 func (*FindRequest) ProtoMessage() {}
 
 func (x *FindRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[31]
+	mi := &file_items_items_proto_msgTypes[33]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2035,7 +2255,7 @@ func (x *FindRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindRequest.ProtoReflect.Descriptor instead.
 func (*FindRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{31}
+	return file_items_items_proto_rawDescGZIP(), []int{33}
 }
 
 func (x *FindRequest) GetSpaceId() string {
@@ -2085,7 +2305,7 @@ type FindResponse struct {
 func (x *FindResponse) Reset() {
 	*x = FindResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[32]
+		mi := &file_items_items_proto_msgTypes[34]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2098,7 +2318,7 @@ func (x *FindResponse) String() string {
 func (*FindResponse) ProtoMessage() {}
 
 func (x *FindResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[32]
+	mi := &file_items_items_proto_msgTypes[34]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2111,7 +2331,7 @@ func (x *FindResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindResponse.ProtoReflect.Descriptor instead.
 func (*FindResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{32}
+	return file_items_items_proto_rawDescGZIP(), []int{34}
 }
 
 func (x *FindResponse) GetItems() []*Item {
@@ -2140,7 +2360,7 @@ type UpdateRequest struct {
 func (x *UpdateRequest) Reset() {
 	*x = UpdateRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[33]
+		mi := &file_items_items_proto_msgTypes[35]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2153,7 +2373,7 @@ func (x *UpdateRequest) String() string {
 func (*UpdateRequest) ProtoMessage() {}
 
 func (x *UpdateRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[33]
+	mi := &file_items_items_proto_msgTypes[35]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2166,7 +2386,7 @@ func (x *UpdateRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead.
 func (*UpdateRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{33}
+	return file_items_items_proto_rawDescGZIP(), []int{35}
 }
 
 func (x *UpdateRequest) GetItem() *Item {
@@ -2195,7 +2415,7 @@ type DeleteRequest struct {
 func (x *DeleteRequest) Reset() {
 	*x = DeleteRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[34]
+		mi := &file_items_items_proto_msgTypes[36]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2208,7 +2428,7 @@ func (x *DeleteRequest) String() string {
 func (*DeleteRequest) ProtoMessage() {}
 
 func (x *DeleteRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[34]
+	mi := &file_items_items_proto_msgTypes[36]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2221,7 +2441,7 @@ func (x *DeleteRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead.
 func (*DeleteRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{34}
+	return file_items_items_proto_rawDescGZIP(), []int{36}
 }
 
 func (x *DeleteRequest) GetItem() *Item {
@@ -2250,7 +2470,7 @@ type UndeleteRequest struct {
 func (x *UndeleteRequest) Reset() {
 	*x = UndeleteRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[35]
+		mi := &file_items_items_proto_msgTypes[37]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2263,7 +2483,7 @@ func (x *UndeleteRequest) String() string {
 func (*UndeleteRequest) ProtoMessage() {}
 
 func (x *UndeleteRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[35]
+	mi := &file_items_items_proto_msgTypes[37]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2276,7 +2496,7 @@ func (x *UndeleteRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UndeleteRequest.ProtoReflect.Descriptor instead.
 func (*UndeleteRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{35}
+	return file_items_items_proto_rawDescGZIP(), []int{37}
 }
 
 func (x *UndeleteRequest) GetItem() *Item {
@@ -2305,7 +2525,7 @@ type PublishRequest struct {
 func (x *PublishRequest) Reset() {
 	*x = PublishRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[36]
+		mi := &file_items_items_proto_msgTypes[38]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2318,7 +2538,7 @@ func (x *PublishRequest) String() string {
 func (*PublishRequest) ProtoMessage() {}
 
 func (x *PublishRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[36]
+	mi := &file_items_items_proto_msgTypes[38]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2331,7 +2551,7 @@ func (x *PublishRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use PublishRequest.ProtoReflect.Descriptor instead.
 func (*PublishRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{36}
+	return file_items_items_proto_rawDescGZIP(), []int{38}
 }
 
 func (x *PublishRequest) GetItem() *Item {
@@ -2360,7 +2580,7 @@ type UnpublishRequest struct {
 func (x *UnpublishRequest) Reset() {
 	*x = UnpublishRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[37]
+		mi := &file_items_items_proto_msgTypes[39]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2373,7 +2593,7 @@ func (x *UnpublishRequest) String() string {
 func (*UnpublishRequest) ProtoMessage() {}
 
 func (x *UnpublishRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[37]
+	mi := &file_items_items_proto_msgTypes[39]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2386,7 +2606,7 @@ func (x *UnpublishRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UnpublishRequest.ProtoReflect.Descriptor instead.
 func (*UnpublishRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{37}
+	return file_items_items_proto_rawDescGZIP(), []int{39}
 }
 
 func (x *UnpublishRequest) GetItem() *Item {
@@ -2418,7 +2638,7 @@ type GetPublishedRequest struct {
 func (x *GetPublishedRequest) Reset() {
 	*x = GetPublishedRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[38]
+		mi := &file_items_items_proto_msgTypes[40]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2431,7 +2651,7 @@ func (x *GetPublishedRequest) String() string {
 func (*GetPublishedRequest) ProtoMessage() {}
 
 func (x *GetPublishedRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[38]
+	mi := &file_items_items_proto_msgTypes[40]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2444,7 +2664,7 @@ func (x *GetPublishedRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetPublishedRequest.ProtoReflect.Descriptor instead.
 func (*GetPublishedRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{38}
+	return file_items_items_proto_rawDescGZIP(), []int{40}
 }
 
 func (x *GetPublishedRequest) GetSpaceId() string {
@@ -2493,7 +2713,7 @@ type GetPublishedResponse struct {
 func (x *GetPublishedResponse) Reset() {
 	*x = GetPublishedResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[39]
+		mi := &file_items_items_proto_msgTypes[41]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2506,7 +2726,7 @@ func (x *GetPublishedResponse) String() string {
 func (*GetPublishedResponse) ProtoMessage() {}
 
 func (x *GetPublishedResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[39]
+	mi := &file_items_items_proto_msgTypes[41]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2519,7 +2739,7 @@ func (x *GetPublishedResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetPublishedResponse.ProtoReflect.Descriptor instead.
 func (*GetPublishedResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{39}
+	return file_items_items_proto_rawDescGZIP(), []int{41}
 }
 
 func (x *GetPublishedResponse) GetItem() *Item {
@@ -2544,7 +2764,7 @@ type FindPublishedRequest struct {
 func (x *FindPublishedRequest) Reset() {
 	*x = FindPublishedRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[40]
+		mi := &file_items_items_proto_msgTypes[42]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2557,7 +2777,7 @@ func (x *FindPublishedRequest) String() string {
 func (*FindPublishedRequest) ProtoMessage() {}
 
 func (x *FindPublishedRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[40]
+	mi := &file_items_items_proto_msgTypes[42]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2570,7 +2790,7 @@ func (x *FindPublishedRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindPublishedRequest.ProtoReflect.Descriptor instead.
 func (*FindPublishedRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{40}
+	return file_items_items_proto_rawDescGZIP(), []int{42}
 }
 
 func (x *FindPublishedRequest) GetSpaceId() string {
@@ -2620,7 +2840,7 @@ type FindPublishedResponse struct {
 func (x *FindPublishedResponse) Reset() {
 	*x = FindPublishedResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[41]
+		mi := &file_items_items_proto_msgTypes[43]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2633,7 +2853,7 @@ func (x *FindPublishedResponse) String() string {
 func (*FindPublishedResponse) ProtoMessage() {}
 
 func (x *FindPublishedResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[41]
+	mi := &file_items_items_proto_msgTypes[43]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2646,7 +2866,7 @@ func (x *FindPublishedResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindPublishedResponse.ProtoReflect.Descriptor instead.
 func (*FindPublishedResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{41}
+	return file_items_items_proto_rawDescGZIP(), []int{43}
 }
 
 func (x *FindPublishedResponse) GetItems() []*Item {
@@ -2678,7 +2898,7 @@ type AggregateRequest struct {
 func (x *AggregateRequest) Reset() {
 	*x = AggregateRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[42]
+		mi := &file_items_items_proto_msgTypes[44]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2691,7 +2911,7 @@ func (x *AggregateRequest) String() string {
 func (*AggregateRequest) ProtoMessage() {}
 
 func (x *AggregateRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[42]
+	mi := &file_items_items_proto_msgTypes[44]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2704,7 +2924,7 @@ func (x *AggregateRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AggregateRequest.ProtoReflect.Descriptor instead.
 func (*AggregateRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{42}
+	return file_items_items_proto_rawDescGZIP(), []int{44}
 }
 
 func (x *AggregateRequest) GetSpaceId() string {
@@ -2755,7 +2975,7 @@ type AggregateResponse struct {
 func (x *AggregateResponse) Reset() {
 	*x = AggregateResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[43]
+		mi := &file_items_items_proto_msgTypes[45]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2768,7 +2988,7 @@ func (x *AggregateResponse) String() string {
 func (*AggregateResponse) ProtoMessage() {}
 
 func (x *AggregateResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[43]
+	mi := &file_items_items_proto_msgTypes[45]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2781,7 +3001,7 @@ func (x *AggregateResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AggregateResponse.ProtoReflect.Descriptor instead.
 func (*AggregateResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{43}
+	return file_items_items_proto_rawDescGZIP(), []int{45}
 }
 
 func (x *AggregateResponse) GetResult() *structpb.Struct {
@@ -2806,7 +3026,7 @@ type AggregatePublishedRequest struct {
 func (x *AggregatePublishedRequest) Reset() {
 	*x = AggregatePublishedRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[44]
+		mi := &file_items_items_proto_msgTypes[46]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2819,7 +3039,7 @@ func (x *AggregatePublishedRequest) String() string {
 func (*AggregatePublishedRequest) ProtoMessage() {}
 
 func (x *AggregatePublishedRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[44]
+	mi := &file_items_items_proto_msgTypes[46]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2832,7 +3052,7 @@ func (x *AggregatePublishedRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AggregatePublishedRequest.ProtoReflect.Descriptor instead.
 func (*AggregatePublishedRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{44}
+	return file_items_items_proto_rawDescGZIP(), []int{46}
 }
 
 func (x *AggregatePublishedRequest) GetSpaceId() string {
@@ -2881,7 +3101,7 @@ type AggregatePublishedResponse struct {
 func (x *AggregatePublishedResponse) Reset() {
 	*x = AggregatePublishedResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[45]
+		mi := &file_items_items_proto_msgTypes[47]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2894,7 +3114,7 @@ func (x *AggregatePublishedResponse) String() string {
 func (*AggregatePublishedResponse) ProtoMessage() {}
 
 func (x *AggregatePublishedResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[45]
+	mi := &file_items_items_proto_msgTypes[47]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2907,7 +3127,7 @@ func (x *AggregatePublishedResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use AggregatePublishedResponse.ProtoReflect.Descriptor instead.
 func (*AggregatePublishedResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{45}
+	return file_items_items_proto_rawDescGZIP(), []int{47}
 }
 
 func (x *AggregatePublishedResponse) GetResult() *structpb.Struct {
@@ -2922,17 +3142,18 @@ type GetRevisionRequest struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	SpaceId      string `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
-	EnvId        string `protobuf:"bytes,2,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
-	CollectionId string `protobuf:"bytes,3,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
-	ItemId       string `protobuf:"bytes,4,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"`
-	RevisionId   string `protobuf:"bytes,5,opt,name=revision_id,json=revisionId,proto3" json:"revision_id,omitempty"`
+	SpaceId      string              `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
+	EnvId        string              `protobuf:"bytes,2,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
+	CollectionId string              `protobuf:"bytes,3,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
+	ItemId       string              `protobuf:"bytes,4,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"`
+	RevisionId   string              `protobuf:"bytes,5,opt,name=revision_id,json=revisionId,proto3" json:"revision_id,omitempty"`
+	Options      *GetRevisionOptions `protobuf:"bytes,10,opt,name=options,proto3" json:"options,omitempty"`
 }
 
 func (x *GetRevisionRequest) Reset() {
 	*x = GetRevisionRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[46]
+		mi := &file_items_items_proto_msgTypes[48]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2945,7 +3166,7 @@ func (x *GetRevisionRequest) String() string {
 func (*GetRevisionRequest) ProtoMessage() {}
 
 func (x *GetRevisionRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[46]
+	mi := &file_items_items_proto_msgTypes[48]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2958,7 +3179,7 @@ func (x *GetRevisionRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetRevisionRequest.ProtoReflect.Descriptor instead.
 func (*GetRevisionRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{46}
+	return file_items_items_proto_rawDescGZIP(), []int{48}
 }
 
 func (x *GetRevisionRequest) GetSpaceId() string {
@@ -2996,6 +3217,13 @@ func (x *GetRevisionRequest) GetRevisionId() string {
 	return ""
 }
 
+func (x *GetRevisionRequest) GetOptions() *GetRevisionOptions {
+	if x != nil {
+		return x.Options
+	}
+	return nil
+}
+
 type GetRevisionResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -3007,7 +3235,7 @@ type GetRevisionResponse struct {
 func (x *GetRevisionResponse) Reset() {
 	*x = GetRevisionResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[47]
+		mi := &file_items_items_proto_msgTypes[49]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3020,7 +3248,7 @@ func (x *GetRevisionResponse) String() string {
 func (*GetRevisionResponse) ProtoMessage() {}
 
 func (x *GetRevisionResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[47]
+	mi := &file_items_items_proto_msgTypes[49]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3033,7 +3261,7 @@ func (x *GetRevisionResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetRevisionResponse.ProtoReflect.Descriptor instead.
 func (*GetRevisionResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{47}
+	return file_items_items_proto_rawDescGZIP(), []int{49}
 }
 
 func (x *GetRevisionResponse) GetItem() *Item {
@@ -3058,7 +3286,7 @@ type ListRevisionsRequest struct {
 func (x *ListRevisionsRequest) Reset() {
 	*x = ListRevisionsRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[48]
+		mi := &file_items_items_proto_msgTypes[50]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3071,7 +3299,7 @@ func (x *ListRevisionsRequest) String() string {
 func (*ListRevisionsRequest) ProtoMessage() {}
 
 func (x *ListRevisionsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[48]
+	mi := &file_items_items_proto_msgTypes[50]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3084,7 +3312,7 @@ func (x *ListRevisionsRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListRevisionsRequest.ProtoReflect.Descriptor instead.
 func (*ListRevisionsRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{48}
+	return file_items_items_proto_rawDescGZIP(), []int{50}
 }
 
 func (x *ListRevisionsRequest) GetSpaceId() string {
@@ -3133,7 +3361,7 @@ type ListRevisionsResponse struct {
 func (x *ListRevisionsResponse) Reset() {
 	*x = ListRevisionsResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[49]
+		mi := &file_items_items_proto_msgTypes[51]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3146,7 +3374,7 @@ func (x *ListRevisionsResponse) String() string {
 func (*ListRevisionsResponse) ProtoMessage() {}
 
 func (x *ListRevisionsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[49]
+	mi := &file_items_items_proto_msgTypes[51]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3159,7 +3387,7 @@ func (x *ListRevisionsResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListRevisionsResponse.ProtoReflect.Descriptor instead.
 func (*ListRevisionsResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{49}
+	return file_items_items_proto_rawDescGZIP(), []int{51}
 }
 
 func (x *ListRevisionsResponse) GetItems() []*Item {
@@ -3182,7 +3410,7 @@ type ArchiveRequest struct {
 func (x *ArchiveRequest) Reset() {
 	*x = ArchiveRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[50]
+		mi := &file_items_items_proto_msgTypes[52]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3195,7 +3423,7 @@ func (x *ArchiveRequest) String() string {
 func (*ArchiveRequest) ProtoMessage() {}
 
 func (x *ArchiveRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[50]
+	mi := &file_items_items_proto_msgTypes[52]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3208,7 +3436,7 @@ func (x *ArchiveRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ArchiveRequest.ProtoReflect.Descriptor instead.
 func (*ArchiveRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{50}
+	return file_items_items_proto_rawDescGZIP(), []int{52}
 }
 
 func (x *ArchiveRequest) GetItem() *Item {
@@ -3229,7 +3457,7 @@ type UnarchiveRequest struct {
 func (x *UnarchiveRequest) Reset() {
 	*x = UnarchiveRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[51]
+		mi := &file_items_items_proto_msgTypes[53]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3242,7 +3470,7 @@ func (x *UnarchiveRequest) String() string {
 func (*UnarchiveRequest) ProtoMessage() {}
 
 func (x *UnarchiveRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[51]
+	mi := &file_items_items_proto_msgTypes[53]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3255,7 +3483,7 @@ func (x *UnarchiveRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UnarchiveRequest.ProtoReflect.Descriptor instead.
 func (*UnarchiveRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{51}
+	return file_items_items_proto_rawDescGZIP(), []int{53}
 }
 
 func (x *UnarchiveRequest) GetItem() *Item {
@@ -3280,7 +3508,7 @@ type FindArchivedRequest struct {
 func (x *FindArchivedRequest) Reset() {
 	*x = FindArchivedRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[52]
+		mi := &file_items_items_proto_msgTypes[54]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3293,7 +3521,7 @@ func (x *FindArchivedRequest) String() string {
 func (*FindArchivedRequest) ProtoMessage() {}
 
 func (x *FindArchivedRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[52]
+	mi := &file_items_items_proto_msgTypes[54]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3306,7 +3534,7 @@ func (x *FindArchivedRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindArchivedRequest.ProtoReflect.Descriptor instead.
 func (*FindArchivedRequest) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{52}
+	return file_items_items_proto_rawDescGZIP(), []int{54}
 }
 
 func (x *FindArchivedRequest) GetSpaceId() string {
@@ -3356,7 +3584,7 @@ type FindArchivedResponse struct {
 func (x *FindArchivedResponse) Reset() {
 	*x = FindArchivedResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_items_items_proto_msgTypes[53]
+		mi := &file_items_items_proto_msgTypes[55]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3369,7 +3597,7 @@ func (x *FindArchivedResponse) String() string {
 func (*FindArchivedResponse) ProtoMessage() {}
 
 func (x *FindArchivedResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_items_items_proto_msgTypes[53]
+	mi := &file_items_items_proto_msgTypes[55]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3382,7 +3610,7 @@ func (x *FindArchivedResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindArchivedResponse.ProtoReflect.Descriptor instead.
 func (*FindArchivedResponse) Descriptor() ([]byte, []int) {
-	return file_items_items_proto_rawDescGZIP(), []int{53}
+	return file_items_items_proto_rawDescGZIP(), []int{55}
 }
 
 func (x *FindArchivedResponse) GetItems() []*Item {
@@ -3437,7 +3665,7 @@ var file_items_items_proto_rawDesc = []byte{
 	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, 0x9c, 0x07, 0x0a, 0x04,
+	0x0a, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0xef, 0x07, 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,
@@ -3466,194 +3694,304 @@ var file_items_items_proto_rawDesc = []byte{
 	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,
+	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, 0x31, 0x0a, 0x14, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x73,
+	0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13,
+	0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 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, 0x12,
+	0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18,
+	0x17, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x53, 0x63, 0x6f,
+	0x72, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18,
+	0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49, 0x64, 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, 0x31, 0x0a, 0x14, 0x72,
-	0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, 0x65, 0x76, 0x69, 0x73,
-	0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 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,
+	0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x65,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x49, 0x64, 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, 0x7d, 0x0a, 0x0b, 0x45,
-	0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70,
+	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, 0xee, 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, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
+	0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+	0x65, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74,
+	0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 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, 0x5d, 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, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f,
+	0x63, 0x61, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64,
+	0x73, 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, 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, 0xdd, 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, 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, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f,
+	0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
+	0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x73, 0x22, 0x8c, 0x01,
+	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, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49,
+	0x64, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61,
+	0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x73, 0x22, 0x8d, 0x01, 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, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49,
+	0x64, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61,
+	0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x73, 0x22, 0x5c, 0x0a, 0x12,
+	0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49, 0x64, 0x12,
+	0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f,
+	0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73,
+	0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 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, 0x54, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x64,
+	0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49, 0x64,
+	0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e,
+	0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x73, 0x22, 0xb1, 0x01, 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, 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, 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,
+	0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x07, 0x6f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 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, 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,
-	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,
+	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, 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,
+	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,
@@ -3662,164 +4000,35 @@ var file_items_items_proto_rawDesc = []byte{
 	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, 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, 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,
+	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,
-	0x42, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	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, 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, 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,
@@ -3828,109 +4037,171 @@ var file_items_items_proto_rawDesc = []byte{
 	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, 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,
+	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, 0xe2, 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, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 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, 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,
-	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, 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, 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,
+	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, 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, 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, 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 (
@@ -3946,8 +4217,8 @@ func file_items_items_proto_rawDescGZIP() []byte {
 }
 
 var file_items_items_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_items_items_proto_msgTypes = make([]protoimpl.MessageInfo, 57)
-var file_items_items_proto_goTypes = []interface{}{
+var file_items_items_proto_msgTypes = make([]protoimpl.MessageInfo, 59)
+var file_items_items_proto_goTypes = []any{
 	(Item_State)(0),                                // 0: content.items.Item.State
 	(*Error)(nil),                                  // 1: content.items.Error
 	(*DecodeError)(nil),                            // 2: content.items.DecodeError
@@ -3972,146 +4243,150 @@ var file_items_items_proto_goTypes = []interface{}{
 	(*FindPublishedOptions)(nil),                   // 21: content.items.FindPublishedOptions
 	(*FindArchivedOptions)(nil),                    // 22: content.items.FindArchivedOptions
 	(*ListRevisionsOptions)(nil),                   // 23: content.items.ListRevisionsOptions
-	(*AggregateOptions)(nil),                       // 24: content.items.AggregateOptions
-	(*AggregatePublishedOptions)(nil),              // 25: content.items.AggregatePublishedOptions
-	(*CreateRequest)(nil),                          // 26: content.items.CreateRequest
-	(*CreateResponse)(nil),                         // 27: content.items.CreateResponse
-	(*IntrospectRequest)(nil),                      // 28: content.items.IntrospectRequest
-	(*IntrospectResponse)(nil),                     // 29: content.items.IntrospectResponse
-	(*GetRequest)(nil),                             // 30: content.items.GetRequest
-	(*GetResponse)(nil),                            // 31: content.items.GetResponse
-	(*FindRequest)(nil),                            // 32: content.items.FindRequest
-	(*FindResponse)(nil),                           // 33: content.items.FindResponse
-	(*UpdateRequest)(nil),                          // 34: content.items.UpdateRequest
-	(*DeleteRequest)(nil),                          // 35: content.items.DeleteRequest
-	(*UndeleteRequest)(nil),                        // 36: content.items.UndeleteRequest
-	(*PublishRequest)(nil),                         // 37: content.items.PublishRequest
-	(*UnpublishRequest)(nil),                       // 38: content.items.UnpublishRequest
-	(*GetPublishedRequest)(nil),                    // 39: content.items.GetPublishedRequest
-	(*GetPublishedResponse)(nil),                   // 40: content.items.GetPublishedResponse
-	(*FindPublishedRequest)(nil),                   // 41: content.items.FindPublishedRequest
-	(*FindPublishedResponse)(nil),                  // 42: content.items.FindPublishedResponse
-	(*AggregateRequest)(nil),                       // 43: content.items.AggregateRequest
-	(*AggregateResponse)(nil),                      // 44: content.items.AggregateResponse
-	(*AggregatePublishedRequest)(nil),              // 45: content.items.AggregatePublishedRequest
-	(*AggregatePublishedResponse)(nil),             // 46: content.items.AggregatePublishedResponse
-	(*GetRevisionRequest)(nil),                     // 47: content.items.GetRevisionRequest
-	(*GetRevisionResponse)(nil),                    // 48: content.items.GetRevisionResponse
-	(*ListRevisionsRequest)(nil),                   // 49: content.items.ListRevisionsRequest
-	(*ListRevisionsResponse)(nil),                  // 50: content.items.ListRevisionsResponse
-	(*ArchiveRequest)(nil),                         // 51: content.items.ArchiveRequest
-	(*UnarchiveRequest)(nil),                       // 52: content.items.UnarchiveRequest
-	(*FindArchivedRequest)(nil),                    // 53: content.items.FindArchivedRequest
-	(*FindArchivedResponse)(nil),                   // 54: content.items.FindArchivedResponse
-	nil,                                            // 55: content.items.Item.TranslationsEntry
-	nil,                                            // 56: content.items.AggregateOptions.FieldsEntry
-	nil,                                            // 57: content.items.AggregatePublishedOptions.FieldsEntry
-	(*timestamppb.Timestamp)(nil),                  // 58: google.protobuf.Timestamp
-	(*structpb.Struct)(nil),                        // 59: google.protobuf.Struct
-	(*common.Filter)(nil),                          // 60: common.Filter
-	(*common.FindOptions)(nil),                     // 61: common.FindOptions
-	(*common.Error_BadRequest_FieldViolation)(nil), // 62: common.Error.BadRequest.FieldViolation
-	(*emptypb.Empty)(nil),                          // 63: google.protobuf.Empty
+	(*GetRevisionOptions)(nil),                     // 24: content.items.GetRevisionOptions
+	(*AggregateOptions)(nil),                       // 25: content.items.AggregateOptions
+	(*AggregatePublishedOptions)(nil),              // 26: content.items.AggregatePublishedOptions
+	(*CreateRequest)(nil),                          // 27: content.items.CreateRequest
+	(*CreateResponse)(nil),                         // 28: content.items.CreateResponse
+	(*IntrospectRequest)(nil),                      // 29: content.items.IntrospectRequest
+	(*IntrospectResponse)(nil),                     // 30: content.items.IntrospectResponse
+	(*GetOptions)(nil),                             // 31: content.items.GetOptions
+	(*GetRequest)(nil),                             // 32: content.items.GetRequest
+	(*GetResponse)(nil),                            // 33: content.items.GetResponse
+	(*FindRequest)(nil),                            // 34: content.items.FindRequest
+	(*FindResponse)(nil),                           // 35: content.items.FindResponse
+	(*UpdateRequest)(nil),                          // 36: content.items.UpdateRequest
+	(*DeleteRequest)(nil),                          // 37: content.items.DeleteRequest
+	(*UndeleteRequest)(nil),                        // 38: content.items.UndeleteRequest
+	(*PublishRequest)(nil),                         // 39: content.items.PublishRequest
+	(*UnpublishRequest)(nil),                       // 40: content.items.UnpublishRequest
+	(*GetPublishedRequest)(nil),                    // 41: content.items.GetPublishedRequest
+	(*GetPublishedResponse)(nil),                   // 42: content.items.GetPublishedResponse
+	(*FindPublishedRequest)(nil),                   // 43: content.items.FindPublishedRequest
+	(*FindPublishedResponse)(nil),                  // 44: content.items.FindPublishedResponse
+	(*AggregateRequest)(nil),                       // 45: content.items.AggregateRequest
+	(*AggregateResponse)(nil),                      // 46: content.items.AggregateResponse
+	(*AggregatePublishedRequest)(nil),              // 47: content.items.AggregatePublishedRequest
+	(*AggregatePublishedResponse)(nil),             // 48: content.items.AggregatePublishedResponse
+	(*GetRevisionRequest)(nil),                     // 49: content.items.GetRevisionRequest
+	(*GetRevisionResponse)(nil),                    // 50: content.items.GetRevisionResponse
+	(*ListRevisionsRequest)(nil),                   // 51: content.items.ListRevisionsRequest
+	(*ListRevisionsResponse)(nil),                  // 52: content.items.ListRevisionsResponse
+	(*ArchiveRequest)(nil),                         // 53: content.items.ArchiveRequest
+	(*UnarchiveRequest)(nil),                       // 54: content.items.UnarchiveRequest
+	(*FindArchivedRequest)(nil),                    // 55: content.items.FindArchivedRequest
+	(*FindArchivedResponse)(nil),                   // 56: content.items.FindArchivedResponse
+	nil,                                            // 57: content.items.Item.TranslationsEntry
+	nil,                                            // 58: content.items.AggregateOptions.FieldsEntry
+	nil,                                            // 59: content.items.AggregatePublishedOptions.FieldsEntry
+	(*timestamppb.Timestamp)(nil),                  // 60: google.protobuf.Timestamp
+	(*structpb.Struct)(nil),                        // 61: google.protobuf.Struct
+	(*common.Filter)(nil),                          // 62: common.Filter
+	(*common.FindOptions)(nil),                     // 63: common.FindOptions
+	(*common.Error_BadRequest_FieldViolation)(nil), // 64: common.Error.BadRequest.FieldViolation
+	(*emptypb.Empty)(nil),                          // 65: google.protobuf.Empty
 }
 var file_items_items_proto_depIdxs = []int32{
 	1,  // 0: content.items.DecodeError.errors:type_name -> content.items.Error
 	1,  // 1: content.items.ValidationError.errors:type_name -> content.items.Error
 	1,  // 2: content.items.ModificationError.errors:type_name -> content.items.Error
 	0,  // 3: content.items.Item.state:type_name -> content.items.Item.State
-	58, // 4: content.items.Item.created_rev_at:type_name -> google.protobuf.Timestamp
-	58, // 5: content.items.Item.created_at:type_name -> google.protobuf.Timestamp
-	58, // 6: content.items.Item.updated_at:type_name -> google.protobuf.Timestamp
-	59, // 7: content.items.Item.data:type_name -> google.protobuf.Struct
-	55, // 8: content.items.Item.translations:type_name -> content.items.Item.TranslationsEntry
-	5,  // 9: content.items.Item.permissions:type_name -> content.items.Permissions
-	60, // 10: content.items.Filter.data:type_name -> common.Filter
-	61, // 11: content.items.FindOptions.options:type_name -> common.FindOptions
-	61, // 12: content.items.FindPublishedOptions.options:type_name -> common.FindOptions
-	61, // 13: content.items.FindArchivedOptions.options:type_name -> common.FindOptions
-	61, // 14: content.items.ListRevisionsOptions.options:type_name -> common.FindOptions
-	56, // 15: content.items.AggregateOptions.fields:type_name -> content.items.AggregateOptions.FieldsEntry
-	57, // 16: content.items.AggregatePublishedOptions.fields:type_name -> content.items.AggregatePublishedOptions.FieldsEntry
+	60, // 4: content.items.Item.created_rev_at:type_name -> google.protobuf.Timestamp
+	60, // 5: content.items.Item.created_at:type_name -> google.protobuf.Timestamp
+	60, // 6: content.items.Item.updated_at:type_name -> google.protobuf.Timestamp
+	61, // 7: content.items.Item.data:type_name -> google.protobuf.Struct
+	5,  // 8: content.items.Item.permissions:type_name -> content.items.Permissions
+	57, // 9: content.items.Item.translations:type_name -> content.items.Item.TranslationsEntry
+	62, // 10: content.items.Filter.data:type_name -> common.Filter
+	63, // 11: content.items.FindOptions.options:type_name -> common.FindOptions
+	63, // 12: content.items.FindPublishedOptions.options:type_name -> common.FindOptions
+	63, // 13: content.items.FindArchivedOptions.options:type_name -> common.FindOptions
+	63, // 14: content.items.ListRevisionsOptions.options:type_name -> common.FindOptions
+	58, // 15: content.items.AggregateOptions.fields:type_name -> content.items.AggregateOptions.FieldsEntry
+	59, // 16: content.items.AggregatePublishedOptions.fields:type_name -> content.items.AggregatePublishedOptions.FieldsEntry
 	6,  // 17: content.items.CreateRequest.item:type_name -> content.items.Item
 	13, // 18: content.items.CreateRequest.options:type_name -> content.items.CreateOptions
 	6,  // 19: content.items.CreateResponse.created:type_name -> content.items.Item
 	6,  // 20: content.items.IntrospectRequest.item:type_name -> content.items.Item
 	6,  // 21: content.items.IntrospectResponse.item:type_name -> content.items.Item
-	62, // 22: content.items.IntrospectResponse.validation_errors:type_name -> common.Error.BadRequest.FieldViolation
-	6,  // 23: content.items.GetResponse.item:type_name -> content.items.Item
-	12, // 24: content.items.FindRequest.filter:type_name -> content.items.Filter
-	14, // 25: content.items.FindRequest.options:type_name -> content.items.FindOptions
-	6,  // 26: content.items.FindResponse.items:type_name -> content.items.Item
-	6,  // 27: content.items.UpdateRequest.item:type_name -> content.items.Item
-	15, // 28: content.items.UpdateRequest.options:type_name -> content.items.UpdateOptions
-	6,  // 29: content.items.DeleteRequest.item:type_name -> content.items.Item
-	17, // 30: content.items.DeleteRequest.options:type_name -> content.items.DeleteOptions
-	6,  // 31: content.items.UndeleteRequest.item:type_name -> content.items.Item
-	18, // 32: content.items.UndeleteRequest.options:type_name -> content.items.UndeleteOptions
-	6,  // 33: content.items.PublishRequest.item:type_name -> content.items.Item
-	19, // 34: content.items.PublishRequest.options:type_name -> content.items.PublishOptions
-	6,  // 35: content.items.UnpublishRequest.item:type_name -> content.items.Item
-	20, // 36: content.items.UnpublishRequest.options:type_name -> content.items.UnpublishOptions
-	16, // 37: content.items.GetPublishedRequest.options:type_name -> content.items.GetPublishedOptions
-	6,  // 38: content.items.GetPublishedResponse.item:type_name -> content.items.Item
-	12, // 39: content.items.FindPublishedRequest.filter:type_name -> content.items.Filter
-	21, // 40: content.items.FindPublishedRequest.options:type_name -> content.items.FindPublishedOptions
-	6,  // 41: content.items.FindPublishedResponse.items:type_name -> content.items.Item
-	12, // 42: content.items.AggregateRequest.filter:type_name -> content.items.Filter
-	24, // 43: content.items.AggregateRequest.options:type_name -> content.items.AggregateOptions
-	59, // 44: content.items.AggregateResponse.result:type_name -> google.protobuf.Struct
-	12, // 45: content.items.AggregatePublishedRequest.filter:type_name -> content.items.Filter
-	25, // 46: content.items.AggregatePublishedRequest.options:type_name -> content.items.AggregatePublishedOptions
-	59, // 47: content.items.AggregatePublishedResponse.result:type_name -> google.protobuf.Struct
-	6,  // 48: content.items.GetRevisionResponse.item:type_name -> content.items.Item
-	23, // 49: content.items.ListRevisionsRequest.options:type_name -> content.items.ListRevisionsOptions
-	6,  // 50: content.items.ListRevisionsResponse.items:type_name -> content.items.Item
-	6,  // 51: content.items.ArchiveRequest.item:type_name -> content.items.Item
-	6,  // 52: content.items.UnarchiveRequest.item:type_name -> content.items.Item
-	12, // 53: content.items.FindArchivedRequest.filter:type_name -> content.items.Filter
-	22, // 54: content.items.FindArchivedRequest.options:type_name -> content.items.FindArchivedOptions
-	6,  // 55: content.items.FindArchivedResponse.items:type_name -> content.items.Item
-	59, // 56: content.items.Item.TranslationsEntry.value:type_name -> google.protobuf.Struct
-	26, // 57: content.items.Items.Create:input_type -> content.items.CreateRequest
-	28, // 58: content.items.Items.Introspect:input_type -> content.items.IntrospectRequest
-	30, // 59: content.items.Items.Get:input_type -> content.items.GetRequest
-	32, // 60: content.items.Items.Find:input_type -> content.items.FindRequest
-	34, // 61: content.items.Items.Update:input_type -> content.items.UpdateRequest
-	35, // 62: content.items.Items.Delete:input_type -> content.items.DeleteRequest
-	36, // 63: content.items.Items.Undelete:input_type -> content.items.UndeleteRequest
-	37, // 64: content.items.Items.Publish:input_type -> content.items.PublishRequest
-	38, // 65: content.items.Items.Unpublish:input_type -> content.items.UnpublishRequest
-	39, // 66: content.items.Items.GetPublished:input_type -> content.items.GetPublishedRequest
-	41, // 67: content.items.Items.FindPublished:input_type -> content.items.FindPublishedRequest
-	43, // 68: content.items.Items.Aggregate:input_type -> content.items.AggregateRequest
-	45, // 69: content.items.Items.AggregatePublished:input_type -> content.items.AggregatePublishedRequest
-	47, // 70: content.items.Items.GetRevision:input_type -> content.items.GetRevisionRequest
-	49, // 71: content.items.Items.ListRevisions:input_type -> content.items.ListRevisionsRequest
-	51, // 72: content.items.Items.Archive:input_type -> content.items.ArchiveRequest
-	53, // 73: content.items.Items.FindArchived:input_type -> content.items.FindArchivedRequest
-	52, // 74: content.items.Items.Unarchive:input_type -> content.items.UnarchiveRequest
-	27, // 75: content.items.Items.Create:output_type -> content.items.CreateResponse
-	29, // 76: content.items.Items.Introspect:output_type -> content.items.IntrospectResponse
-	31, // 77: content.items.Items.Get:output_type -> content.items.GetResponse
-	33, // 78: content.items.Items.Find:output_type -> content.items.FindResponse
-	63, // 79: content.items.Items.Update:output_type -> google.protobuf.Empty
-	63, // 80: content.items.Items.Delete:output_type -> google.protobuf.Empty
-	63, // 81: content.items.Items.Undelete:output_type -> google.protobuf.Empty
-	63, // 82: content.items.Items.Publish:output_type -> google.protobuf.Empty
-	63, // 83: content.items.Items.Unpublish:output_type -> google.protobuf.Empty
-	40, // 84: content.items.Items.GetPublished:output_type -> content.items.GetPublishedResponse
-	42, // 85: content.items.Items.FindPublished:output_type -> content.items.FindPublishedResponse
-	44, // 86: content.items.Items.Aggregate:output_type -> content.items.AggregateResponse
-	46, // 87: content.items.Items.AggregatePublished:output_type -> content.items.AggregatePublishedResponse
-	48, // 88: content.items.Items.GetRevision:output_type -> content.items.GetRevisionResponse
-	50, // 89: content.items.Items.ListRevisions:output_type -> content.items.ListRevisionsResponse
-	63, // 90: content.items.Items.Archive:output_type -> google.protobuf.Empty
-	54, // 91: content.items.Items.FindArchived:output_type -> content.items.FindArchivedResponse
-	63, // 92: content.items.Items.Unarchive:output_type -> google.protobuf.Empty
-	75, // [75:93] is the sub-list for method output_type
-	57, // [57:75] is the sub-list for method input_type
-	57, // [57:57] is the sub-list for extension type_name
-	57, // [57:57] is the sub-list for extension extendee
-	0,  // [0:57] is the sub-list for field type_name
+	64, // 22: content.items.IntrospectResponse.validation_errors:type_name -> common.Error.BadRequest.FieldViolation
+	31, // 23: content.items.GetRequest.options:type_name -> content.items.GetOptions
+	6,  // 24: content.items.GetResponse.item:type_name -> content.items.Item
+	12, // 25: content.items.FindRequest.filter:type_name -> content.items.Filter
+	14, // 26: content.items.FindRequest.options:type_name -> content.items.FindOptions
+	6,  // 27: content.items.FindResponse.items:type_name -> content.items.Item
+	6,  // 28: content.items.UpdateRequest.item:type_name -> content.items.Item
+	15, // 29: content.items.UpdateRequest.options:type_name -> content.items.UpdateOptions
+	6,  // 30: content.items.DeleteRequest.item:type_name -> content.items.Item
+	17, // 31: content.items.DeleteRequest.options:type_name -> content.items.DeleteOptions
+	6,  // 32: content.items.UndeleteRequest.item:type_name -> content.items.Item
+	18, // 33: content.items.UndeleteRequest.options:type_name -> content.items.UndeleteOptions
+	6,  // 34: content.items.PublishRequest.item:type_name -> content.items.Item
+	19, // 35: content.items.PublishRequest.options:type_name -> content.items.PublishOptions
+	6,  // 36: content.items.UnpublishRequest.item:type_name -> content.items.Item
+	20, // 37: content.items.UnpublishRequest.options:type_name -> content.items.UnpublishOptions
+	16, // 38: content.items.GetPublishedRequest.options:type_name -> content.items.GetPublishedOptions
+	6,  // 39: content.items.GetPublishedResponse.item:type_name -> content.items.Item
+	12, // 40: content.items.FindPublishedRequest.filter:type_name -> content.items.Filter
+	21, // 41: content.items.FindPublishedRequest.options:type_name -> content.items.FindPublishedOptions
+	6,  // 42: content.items.FindPublishedResponse.items:type_name -> content.items.Item
+	12, // 43: content.items.AggregateRequest.filter:type_name -> content.items.Filter
+	25, // 44: content.items.AggregateRequest.options:type_name -> content.items.AggregateOptions
+	61, // 45: content.items.AggregateResponse.result:type_name -> google.protobuf.Struct
+	12, // 46: content.items.AggregatePublishedRequest.filter:type_name -> content.items.Filter
+	26, // 47: content.items.AggregatePublishedRequest.options:type_name -> content.items.AggregatePublishedOptions
+	61, // 48: content.items.AggregatePublishedResponse.result:type_name -> google.protobuf.Struct
+	24, // 49: content.items.GetRevisionRequest.options:type_name -> content.items.GetRevisionOptions
+	6,  // 50: content.items.GetRevisionResponse.item:type_name -> content.items.Item
+	23, // 51: content.items.ListRevisionsRequest.options:type_name -> content.items.ListRevisionsOptions
+	6,  // 52: content.items.ListRevisionsResponse.items:type_name -> content.items.Item
+	6,  // 53: content.items.ArchiveRequest.item:type_name -> content.items.Item
+	6,  // 54: content.items.UnarchiveRequest.item:type_name -> content.items.Item
+	12, // 55: content.items.FindArchivedRequest.filter:type_name -> content.items.Filter
+	22, // 56: content.items.FindArchivedRequest.options:type_name -> content.items.FindArchivedOptions
+	6,  // 57: content.items.FindArchivedResponse.items:type_name -> content.items.Item
+	61, // 58: content.items.Item.TranslationsEntry.value:type_name -> google.protobuf.Struct
+	27, // 59: content.items.Items.Create:input_type -> content.items.CreateRequest
+	29, // 60: content.items.Items.Introspect:input_type -> content.items.IntrospectRequest
+	32, // 61: content.items.Items.Get:input_type -> content.items.GetRequest
+	34, // 62: content.items.Items.Find:input_type -> content.items.FindRequest
+	36, // 63: content.items.Items.Update:input_type -> content.items.UpdateRequest
+	37, // 64: content.items.Items.Delete:input_type -> content.items.DeleteRequest
+	38, // 65: content.items.Items.Undelete:input_type -> content.items.UndeleteRequest
+	39, // 66: content.items.Items.Publish:input_type -> content.items.PublishRequest
+	40, // 67: content.items.Items.Unpublish:input_type -> content.items.UnpublishRequest
+	41, // 68: content.items.Items.GetPublished:input_type -> content.items.GetPublishedRequest
+	43, // 69: content.items.Items.FindPublished:input_type -> content.items.FindPublishedRequest
+	45, // 70: content.items.Items.Aggregate:input_type -> content.items.AggregateRequest
+	47, // 71: content.items.Items.AggregatePublished:input_type -> content.items.AggregatePublishedRequest
+	49, // 72: content.items.Items.GetRevision:input_type -> content.items.GetRevisionRequest
+	51, // 73: content.items.Items.ListRevisions:input_type -> content.items.ListRevisionsRequest
+	53, // 74: content.items.Items.Archive:input_type -> content.items.ArchiveRequest
+	55, // 75: content.items.Items.FindArchived:input_type -> content.items.FindArchivedRequest
+	54, // 76: content.items.Items.Unarchive:input_type -> content.items.UnarchiveRequest
+	28, // 77: content.items.Items.Create:output_type -> content.items.CreateResponse
+	30, // 78: content.items.Items.Introspect:output_type -> content.items.IntrospectResponse
+	33, // 79: content.items.Items.Get:output_type -> content.items.GetResponse
+	35, // 80: content.items.Items.Find:output_type -> content.items.FindResponse
+	65, // 81: content.items.Items.Update:output_type -> google.protobuf.Empty
+	65, // 82: content.items.Items.Delete:output_type -> google.protobuf.Empty
+	65, // 83: content.items.Items.Undelete:output_type -> google.protobuf.Empty
+	65, // 84: content.items.Items.Publish:output_type -> google.protobuf.Empty
+	65, // 85: content.items.Items.Unpublish:output_type -> google.protobuf.Empty
+	42, // 86: content.items.Items.GetPublished:output_type -> content.items.GetPublishedResponse
+	44, // 87: content.items.Items.FindPublished:output_type -> content.items.FindPublishedResponse
+	46, // 88: content.items.Items.Aggregate:output_type -> content.items.AggregateResponse
+	48, // 89: content.items.Items.AggregatePublished:output_type -> content.items.AggregatePublishedResponse
+	50, // 90: content.items.Items.GetRevision:output_type -> content.items.GetRevisionResponse
+	52, // 91: content.items.Items.ListRevisions:output_type -> content.items.ListRevisionsResponse
+	65, // 92: content.items.Items.Archive:output_type -> google.protobuf.Empty
+	56, // 93: content.items.Items.FindArchived:output_type -> content.items.FindArchivedResponse
+	65, // 94: content.items.Items.Unarchive:output_type -> google.protobuf.Empty
+	77, // [77:95] is the sub-list for method output_type
+	59, // [59:77] is the sub-list for method input_type
+	59, // [59:59] is the sub-list for extension type_name
+	59, // [59:59] is the sub-list for extension extendee
+	0,  // [0:59] is the sub-list for field type_name
 }
 
 func init() { file_items_items_proto_init() }
@@ -4120,7 +4395,7 @@ func file_items_items_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_items_items_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Error); i {
 			case 0:
 				return &v.state
@@ -4132,7 +4407,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*DecodeError); i {
 			case 0:
 				return &v.state
@@ -4144,7 +4419,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*ValidationError); i {
 			case 0:
 				return &v.state
@@ -4156,7 +4431,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*ModificationError); i {
 			case 0:
 				return &v.state
@@ -4168,7 +4443,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*Permissions); i {
 			case 0:
 				return &v.state
@@ -4180,7 +4455,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*Item); i {
 			case 0:
 				return &v.state
@@ -4192,7 +4467,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*EventCreate); i {
 			case 0:
 				return &v.state
@@ -4204,7 +4479,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*EventUpdate); i {
 			case 0:
 				return &v.state
@@ -4216,7 +4491,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*EventPublish); i {
 			case 0:
 				return &v.state
@@ -4228,7 +4503,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*EventUnpublish); i {
 			case 0:
 				return &v.state
@@ -4240,7 +4515,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*EventDelete); i {
 			case 0:
 				return &v.state
@@ -4252,7 +4527,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -4264,7 +4539,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateOptions); i {
 			case 0:
 				return &v.state
@@ -4276,7 +4551,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*FindOptions); i {
 			case 0:
 				return &v.state
@@ -4288,7 +4563,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateOptions); i {
 			case 0:
 				return &v.state
@@ -4300,7 +4575,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[15].Exporter = func(v any, i int) any {
 			switch v := v.(*GetPublishedOptions); i {
 			case 0:
 				return &v.state
@@ -4312,7 +4587,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[16].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteOptions); i {
 			case 0:
 				return &v.state
@@ -4324,7 +4599,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[17].Exporter = func(v any, i int) any {
 			switch v := v.(*UndeleteOptions); i {
 			case 0:
 				return &v.state
@@ -4336,7 +4611,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[18].Exporter = func(v any, i int) any {
 			switch v := v.(*PublishOptions); i {
 			case 0:
 				return &v.state
@@ -4348,7 +4623,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[19].Exporter = func(v any, i int) any {
 			switch v := v.(*UnpublishOptions); i {
 			case 0:
 				return &v.state
@@ -4360,7 +4635,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[20].Exporter = func(v any, i int) any {
 			switch v := v.(*FindPublishedOptions); i {
 			case 0:
 				return &v.state
@@ -4372,7 +4647,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[21].Exporter = func(v any, i int) any {
 			switch v := v.(*FindArchivedOptions); i {
 			case 0:
 				return &v.state
@@ -4384,7 +4659,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[22].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRevisionsOptions); i {
 			case 0:
 				return &v.state
@@ -4396,7 +4671,19 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[23].Exporter = func(v any, i int) any {
+			switch v := v.(*GetRevisionOptions); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_items_items_proto_msgTypes[24].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregateOptions); i {
 			case 0:
 				return &v.state
@@ -4408,7 +4695,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[25].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregatePublishedOptions); i {
 			case 0:
 				return &v.state
@@ -4420,7 +4707,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[26].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -4432,7 +4719,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[27].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -4444,7 +4731,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[28].Exporter = func(v any, i int) any {
 			switch v := v.(*IntrospectRequest); i {
 			case 0:
 				return &v.state
@@ -4456,7 +4743,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[29].Exporter = func(v any, i int) any {
 			switch v := v.(*IntrospectResponse); i {
 			case 0:
 				return &v.state
@@ -4468,7 +4755,19 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[30].Exporter = func(v any, i int) any {
+			switch v := v.(*GetOptions); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_items_items_proto_msgTypes[31].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -4480,7 +4779,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[32].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -4492,7 +4791,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[33].Exporter = func(v any, i int) any {
 			switch v := v.(*FindRequest); i {
 			case 0:
 				return &v.state
@@ -4504,7 +4803,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[34].Exporter = func(v any, i int) any {
 			switch v := v.(*FindResponse); i {
 			case 0:
 				return &v.state
@@ -4516,7 +4815,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[35].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -4528,7 +4827,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[36].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -4540,7 +4839,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[37].Exporter = func(v any, i int) any {
 			switch v := v.(*UndeleteRequest); i {
 			case 0:
 				return &v.state
@@ -4552,7 +4851,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[38].Exporter = func(v any, i int) any {
 			switch v := v.(*PublishRequest); i {
 			case 0:
 				return &v.state
@@ -4564,7 +4863,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[39].Exporter = func(v any, i int) any {
 			switch v := v.(*UnpublishRequest); i {
 			case 0:
 				return &v.state
@@ -4576,7 +4875,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[40].Exporter = func(v any, i int) any {
 			switch v := v.(*GetPublishedRequest); i {
 			case 0:
 				return &v.state
@@ -4588,7 +4887,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[41].Exporter = func(v any, i int) any {
 			switch v := v.(*GetPublishedResponse); i {
 			case 0:
 				return &v.state
@@ -4600,7 +4899,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[42].Exporter = func(v any, i int) any {
 			switch v := v.(*FindPublishedRequest); i {
 			case 0:
 				return &v.state
@@ -4612,7 +4911,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[43].Exporter = func(v any, i int) any {
 			switch v := v.(*FindPublishedResponse); i {
 			case 0:
 				return &v.state
@@ -4624,7 +4923,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[44].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregateRequest); i {
 			case 0:
 				return &v.state
@@ -4636,7 +4935,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[45].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregateResponse); i {
 			case 0:
 				return &v.state
@@ -4648,7 +4947,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[46].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregatePublishedRequest); i {
 			case 0:
 				return &v.state
@@ -4660,7 +4959,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[47].Exporter = func(v any, i int) any {
 			switch v := v.(*AggregatePublishedResponse); i {
 			case 0:
 				return &v.state
@@ -4672,7 +4971,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[48].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRevisionRequest); i {
 			case 0:
 				return &v.state
@@ -4684,7 +4983,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[49].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRevisionResponse); i {
 			case 0:
 				return &v.state
@@ -4696,7 +4995,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[50].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRevisionsRequest); i {
 			case 0:
 				return &v.state
@@ -4708,7 +5007,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[51].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRevisionsResponse); i {
 			case 0:
 				return &v.state
@@ -4720,7 +5019,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[52].Exporter = func(v any, i int) any {
 			switch v := v.(*ArchiveRequest); i {
 			case 0:
 				return &v.state
@@ -4732,7 +5031,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[53].Exporter = func(v any, i int) any {
 			switch v := v.(*UnarchiveRequest); i {
 			case 0:
 				return &v.state
@@ -4744,7 +5043,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[54].Exporter = func(v any, i int) any {
 			switch v := v.(*FindArchivedRequest); i {
 			case 0:
 				return &v.state
@@ -4756,7 +5055,7 @@ func file_items_items_proto_init() {
 				return nil
 			}
 		}
-		file_items_items_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} {
+		file_items_items_proto_msgTypes[55].Exporter = func(v any, i int) any {
 			switch v := v.(*FindArchivedResponse); i {
 			case 0:
 				return &v.state
@@ -4775,7 +5074,7 @@ func file_items_items_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_items_items_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   57,
+			NumMessages:   59,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/proto/items/items_grpc.pb.go b/proto/items/items_grpc.pb.go
index b246b7627a8bbbdaaba51165e9f7b7e936691a11..16b486427fef5d6d4ee0dfd75c80da606413273d 100644
--- a/proto/items/items_grpc.pb.go
+++ b/proto/items/items_grpc.pb.go
@@ -8,8 +8,8 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: items/items.proto
 
 package items
@@ -24,8 +24,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Items_Create_FullMethodName             = "/content.items.Items/Create"
@@ -51,6 +51,9 @@ const (
 // ItemsClient is the client API for Items 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.
+//
+// *
+// Сервис API элементов
 type ItemsClient interface {
 	// *
 	// Создать запись
@@ -93,8 +96,9 @@ func NewItemsClient(cc grpc.ClientConnInterface) ItemsClient {
 }
 
 func (c *itemsClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Items_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -102,8 +106,9 @@ func (c *itemsClient) Create(ctx context.Context, in *CreateRequest, opts ...grp
 }
 
 func (c *itemsClient) Introspect(ctx context.Context, in *IntrospectRequest, opts ...grpc.CallOption) (*IntrospectResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(IntrospectResponse)
-	err := c.cc.Invoke(ctx, Items_Introspect_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Introspect_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -111,8 +116,9 @@ func (c *itemsClient) Introspect(ctx context.Context, in *IntrospectRequest, opt
 }
 
 func (c *itemsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Items_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -120,8 +126,9 @@ func (c *itemsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Call
 }
 
 func (c *itemsClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindResponse)
-	err := c.cc.Invoke(ctx, Items_Find_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Find_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -129,8 +136,9 @@ func (c *itemsClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.Ca
 }
 
 func (c *itemsClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -138,8 +146,9 @@ func (c *itemsClient) Update(ctx context.Context, in *UpdateRequest, opts ...grp
 }
 
 func (c *itemsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -147,8 +156,9 @@ func (c *itemsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grp
 }
 
 func (c *itemsClient) Undelete(ctx context.Context, in *UndeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Undelete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Undelete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -156,8 +166,9 @@ func (c *itemsClient) Undelete(ctx context.Context, in *UndeleteRequest, opts ..
 }
 
 func (c *itemsClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Publish_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Publish_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -165,8 +176,9 @@ func (c *itemsClient) Publish(ctx context.Context, in *PublishRequest, opts ...g
 }
 
 func (c *itemsClient) Unpublish(ctx context.Context, in *UnpublishRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Unpublish_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Unpublish_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -174,8 +186,9 @@ func (c *itemsClient) Unpublish(ctx context.Context, in *UnpublishRequest, opts
 }
 
 func (c *itemsClient) GetPublished(ctx context.Context, in *GetPublishedRequest, opts ...grpc.CallOption) (*GetPublishedResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetPublishedResponse)
-	err := c.cc.Invoke(ctx, Items_GetPublished_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_GetPublished_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -183,8 +196,9 @@ func (c *itemsClient) GetPublished(ctx context.Context, in *GetPublishedRequest,
 }
 
 func (c *itemsClient) FindPublished(ctx context.Context, in *FindPublishedRequest, opts ...grpc.CallOption) (*FindPublishedResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindPublishedResponse)
-	err := c.cc.Invoke(ctx, Items_FindPublished_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_FindPublished_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -192,8 +206,9 @@ func (c *itemsClient) FindPublished(ctx context.Context, in *FindPublishedReques
 }
 
 func (c *itemsClient) Aggregate(ctx context.Context, in *AggregateRequest, opts ...grpc.CallOption) (*AggregateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(AggregateResponse)
-	err := c.cc.Invoke(ctx, Items_Aggregate_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Aggregate_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -201,8 +216,9 @@ func (c *itemsClient) Aggregate(ctx context.Context, in *AggregateRequest, opts
 }
 
 func (c *itemsClient) AggregatePublished(ctx context.Context, in *AggregatePublishedRequest, opts ...grpc.CallOption) (*AggregatePublishedResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(AggregatePublishedResponse)
-	err := c.cc.Invoke(ctx, Items_AggregatePublished_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_AggregatePublished_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -210,8 +226,9 @@ func (c *itemsClient) AggregatePublished(ctx context.Context, in *AggregatePubli
 }
 
 func (c *itemsClient) GetRevision(ctx context.Context, in *GetRevisionRequest, opts ...grpc.CallOption) (*GetRevisionResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetRevisionResponse)
-	err := c.cc.Invoke(ctx, Items_GetRevision_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_GetRevision_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -219,8 +236,9 @@ func (c *itemsClient) GetRevision(ctx context.Context, in *GetRevisionRequest, o
 }
 
 func (c *itemsClient) ListRevisions(ctx context.Context, in *ListRevisionsRequest, opts ...grpc.CallOption) (*ListRevisionsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListRevisionsResponse)
-	err := c.cc.Invoke(ctx, Items_ListRevisions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_ListRevisions_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -228,8 +246,9 @@ func (c *itemsClient) ListRevisions(ctx context.Context, in *ListRevisionsReques
 }
 
 func (c *itemsClient) Archive(ctx context.Context, in *ArchiveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Archive_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Archive_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -237,8 +256,9 @@ func (c *itemsClient) Archive(ctx context.Context, in *ArchiveRequest, opts ...g
 }
 
 func (c *itemsClient) FindArchived(ctx context.Context, in *FindArchivedRequest, opts ...grpc.CallOption) (*FindArchivedResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindArchivedResponse)
-	err := c.cc.Invoke(ctx, Items_FindArchived_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_FindArchived_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -246,8 +266,9 @@ func (c *itemsClient) FindArchived(ctx context.Context, in *FindArchivedRequest,
 }
 
 func (c *itemsClient) Unarchive(ctx context.Context, in *UnarchiveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Items_Unarchive_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Items_Unarchive_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -256,7 +277,10 @@ func (c *itemsClient) Unarchive(ctx context.Context, in *UnarchiveRequest, opts
 
 // ItemsServer is the server API for Items service.
 // All implementations must embed UnimplementedItemsServer
-// for forward compatibility
+// for forward compatibility.
+//
+// *
+// Сервис API элементов
 type ItemsServer interface {
 	// *
 	// Создать запись
@@ -291,9 +315,12 @@ type ItemsServer interface {
 	mustEmbedUnimplementedItemsServer()
 }
 
-// UnimplementedItemsServer must be embedded to have forward compatible implementations.
-type UnimplementedItemsServer struct {
-}
+// UnimplementedItemsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedItemsServer struct{}
 
 func (UnimplementedItemsServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -350,6 +377,7 @@ func (UnimplementedItemsServer) Unarchive(context.Context, *UnarchiveRequest) (*
 	return nil, status.Errorf(codes.Unimplemented, "method Unarchive not implemented")
 }
 func (UnimplementedItemsServer) mustEmbedUnimplementedItemsServer() {}
+func (UnimplementedItemsServer) testEmbeddedByValue()               {}
 
 // UnsafeItemsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to ItemsServer will
@@ -359,6 +387,13 @@ type UnsafeItemsServer interface {
 }
 
 func RegisterItemsServer(s grpc.ServiceRegistrar, srv ItemsServer) {
+	// If the following call pancis, it indicates UnimplementedItemsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Items_ServiceDesc, srv)
 }
 
diff --git a/proto/locales/locales.pb.go b/proto/locales/locales.pb.go
index 6ed197b9a1d42660f0211daaaba01f7334ff93df..a1a301ae6e8acc273ac186745f14df700e2478c4 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: locales/locales.proto
 
 package locales
@@ -26,9 +26,16 @@ type Locale struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Id      string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
-	SpaceId string `protobuf:"bytes,2,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
-	Name    string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+	Id         string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`                                   // Идентификатор локали, генерируется автоматически. Для локали по умолчанию устанавливается как "default".
+	SpaceId    string `protobuf:"bytes,2,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`          // Идентификатор пространства.
+	Name       string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`                               // Название локали. Опционально, заполняется автоматически (Пример: russian, english)
+	NativeName string `protobuf:"bytes,4,opt,name=native_name,json=nativeName,proto3" json:"native_name,omitempty"` // Название локали на языке локали. Опционально, заполняется автоматически  (Пример: Русский, English)
+	Code       string `protobuf:"bytes,5,opt,name=code,proto3" json:"code,omitempty"`                               // Код локали https://en.wikipedia.org/wiki/IETF_language_tag
+	Fallback   string `protobuf:"bytes,6,opt,name=fallback,proto3" json:"fallback,omitempty"`                       // Идентификатор локали, который будет использоваться при отсутствии перевода
+	Direction  string `protobuf:"bytes,7,opt,name=direction,proto3" json:"direction,omitempty"`                     // Направление письма - слева направо (ltr) или справа налево (rtl). По умолчанию устанавливается ltr.
+	Weight     int64  `protobuf:"varint,8,opt,name=weight,proto3" json:"weight,omitempty"`                          // Вес локали.
+	NoPublish  bool   `protobuf:"varint,100,opt,name=no_publish,json=noPublish,proto3" json:"no_publish,omitempty"` // Не публиковать контент данной локали. Не будет доступен контент через Delivery API. (кроме default)
+	Disabled   bool   `protobuf:"varint,101,opt,name=disabled,proto3" json:"disabled,omitempty"`                    // Запретить использование локали. Нельзя создавать и редактировать контент для данной локали (кроме default)
 }
 
 func (x *Locale) Reset() {
@@ -84,6 +91,55 @@ func (x *Locale) GetName() string {
 	return ""
 }
 
+func (x *Locale) GetNativeName() string {
+	if x != nil {
+		return x.NativeName
+	}
+	return ""
+}
+
+func (x *Locale) GetCode() string {
+	if x != nil {
+		return x.Code
+	}
+	return ""
+}
+
+func (x *Locale) GetFallback() string {
+	if x != nil {
+		return x.Fallback
+	}
+	return ""
+}
+
+func (x *Locale) GetDirection() string {
+	if x != nil {
+		return x.Direction
+	}
+	return ""
+}
+
+func (x *Locale) GetWeight() int64 {
+	if x != nil {
+		return x.Weight
+	}
+	return 0
+}
+
+func (x *Locale) GetNoPublish() bool {
+	if x != nil {
+		return x.NoPublish
+	}
+	return false
+}
+
+func (x *Locale) GetDisabled() bool {
+	if x != nil {
+		return x.Disabled
+	}
+	return false
+}
+
 type CreateRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -178,6 +234,53 @@ func (x *CreateResponse) GetLocale() *Locale {
 	return nil
 }
 
+type UpdateRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Locale *Locale `protobuf:"bytes,1,opt,name=locale,proto3" json:"locale,omitempty"`
+}
+
+func (x *UpdateRequest) Reset() {
+	*x = UpdateRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_locales_locales_proto_msgTypes[3]
+		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_locales_locales_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 UpdateRequest.ProtoReflect.Descriptor instead.
+func (*UpdateRequest) Descriptor() ([]byte, []int) {
+	return file_locales_locales_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *UpdateRequest) GetLocale() *Locale {
+	if x != nil {
+		return x.Locale
+	}
+	return nil
+}
+
 type ListRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -189,7 +292,7 @@ type ListRequest struct {
 func (x *ListRequest) Reset() {
 	*x = ListRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_locales_locales_proto_msgTypes[3]
+		mi := &file_locales_locales_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -202,7 +305,7 @@ func (x *ListRequest) String() string {
 func (*ListRequest) ProtoMessage() {}
 
 func (x *ListRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_locales_locales_proto_msgTypes[3]
+	mi := &file_locales_locales_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -215,7 +318,7 @@ func (x *ListRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListRequest.ProtoReflect.Descriptor instead.
 func (*ListRequest) Descriptor() ([]byte, []int) {
-	return file_locales_locales_proto_rawDescGZIP(), []int{3}
+	return file_locales_locales_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *ListRequest) GetSpaceId() string {
@@ -236,7 +339,7 @@ type ListResponse struct {
 func (x *ListResponse) Reset() {
 	*x = ListResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_locales_locales_proto_msgTypes[4]
+		mi := &file_locales_locales_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -249,7 +352,7 @@ func (x *ListResponse) String() string {
 func (*ListResponse) ProtoMessage() {}
 
 func (x *ListResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_locales_locales_proto_msgTypes[4]
+	mi := &file_locales_locales_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -262,7 +365,7 @@ func (x *ListResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListResponse.ProtoReflect.Descriptor instead.
 func (*ListResponse) Descriptor() ([]byte, []int) {
-	return file_locales_locales_proto_rawDescGZIP(), []int{4}
+	return file_locales_locales_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *ListResponse) GetLocales() []*Locale {
@@ -277,14 +380,14 @@ type DeleteRequest struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	SpaceId  string `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
-	LocaleId string `protobuf:"bytes,2,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`
+	Id      string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	SpaceId string `protobuf:"bytes,2,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
 }
 
 func (x *DeleteRequest) Reset() {
 	*x = DeleteRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_locales_locales_proto_msgTypes[5]
+		mi := &file_locales_locales_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -297,7 +400,7 @@ func (x *DeleteRequest) String() string {
 func (*DeleteRequest) ProtoMessage() {}
 
 func (x *DeleteRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_locales_locales_proto_msgTypes[5]
+	mi := &file_locales_locales_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -310,19 +413,19 @@ func (x *DeleteRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead.
 func (*DeleteRequest) Descriptor() ([]byte, []int) {
-	return file_locales_locales_proto_rawDescGZIP(), []int{5}
+	return file_locales_locales_proto_rawDescGZIP(), []int{6}
 }
 
-func (x *DeleteRequest) GetSpaceId() string {
+func (x *DeleteRequest) GetId() string {
 	if x != nil {
-		return x.SpaceId
+		return x.Id
 	}
 	return ""
 }
 
-func (x *DeleteRequest) GetLocaleId() string {
+func (x *DeleteRequest) GetSpaceId() string {
 	if x != nil {
-		return x.LocaleId
+		return x.SpaceId
 	}
 	return ""
 }
@@ -334,49 +437,69 @@ var file_locales_locales_proto_rawDesc = []byte{
 	0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
 	0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 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, 0x22, 0x47, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 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, 0x12, 0x0a, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x40,
-	0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x2f, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x17, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
-	0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
-	0x22, 0x41, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x89, 0x02, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
+	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, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x1f, 0x0a, 0x0b, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+	0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x63, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
+	0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16,
+	0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06,
+	0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x6f, 0x5f, 0x70, 0x75, 0x62,
+	0x6c, 0x69, 0x73, 0x68, 0x18, 0x64, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6e, 0x6f, 0x50, 0x75,
+	0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
+	0x64, 0x18, 0x65, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
+	0x64, 0x22, 0x40, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63,
 	0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x52, 0x06, 0x6c, 0x6f, 0x63,
-	0x61, 0x6c, 0x65, 0x22, 0x28, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 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, 0x22, 0x41, 0x0a,
-	0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a,
-	0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17,
-	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73,
-	0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73,
-	0x22, 0x47, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 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, 0x1b, 0x0a, 0x09,
-	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49, 0x64, 0x32, 0xe1, 0x01, 0x0a, 0x07, 0x4c, 0x6f,
-	0x63, 0x61, 0x6c, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12,
+	0x61, 0x6c, 0x65, 0x22, 0x41, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x52, 0x06,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x22, 0x40, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
+	0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x22, 0x28, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 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, 0x22, 0x41, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f,
+	0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x52, 0x07, 0x6c, 0x6f,
+	0x63, 0x61, 0x6c, 0x65, 0x73, 0x22, 0x3a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 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, 0x32, 0xa5, 0x02, 0x0a, 0x07, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x12, 0x4b, 0x0a,
+	0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x06, 0x55, 0x70,
+	0x64, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c,
+	0x6f, 0x63, 0x61, 0x6c, 0x65, 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, 0x45,
+	0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c,
+	0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12,
 	0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
-	0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65,
-	0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x00, 0x12, 0x45, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73,
-	0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x06, 0x44, 0x65, 0x6c,
-	0x65, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x6c, 0x6f,
-	0x63, 0x61, 0x6c, 0x65, 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, 0x42, 0x34, 0x5a,
-	0x32, 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, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x3b, 0x6c, 0x6f, 0x63, 0x61,
-	0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	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, 0x42, 0x34, 0x5a, 0x32, 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,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x3b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x73, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -391,31 +514,35 @@ func file_locales_locales_proto_rawDescGZIP() []byte {
 	return file_locales_locales_proto_rawDescData
 }
 
-var file_locales_locales_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
-var file_locales_locales_proto_goTypes = []interface{}{
+var file_locales_locales_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_locales_locales_proto_goTypes = []any{
 	(*Locale)(nil),         // 0: content.locales.Locale
 	(*CreateRequest)(nil),  // 1: content.locales.CreateRequest
 	(*CreateResponse)(nil), // 2: content.locales.CreateResponse
-	(*ListRequest)(nil),    // 3: content.locales.ListRequest
-	(*ListResponse)(nil),   // 4: content.locales.ListResponse
-	(*DeleteRequest)(nil),  // 5: content.locales.DeleteRequest
-	(*emptypb.Empty)(nil),  // 6: google.protobuf.Empty
+	(*UpdateRequest)(nil),  // 3: content.locales.UpdateRequest
+	(*ListRequest)(nil),    // 4: content.locales.ListRequest
+	(*ListResponse)(nil),   // 5: content.locales.ListResponse
+	(*DeleteRequest)(nil),  // 6: content.locales.DeleteRequest
+	(*emptypb.Empty)(nil),  // 7: google.protobuf.Empty
 }
 var file_locales_locales_proto_depIdxs = []int32{
 	0, // 0: content.locales.CreateRequest.locale:type_name -> content.locales.Locale
 	0, // 1: content.locales.CreateResponse.locale:type_name -> content.locales.Locale
-	0, // 2: content.locales.ListResponse.locales:type_name -> content.locales.Locale
-	1, // 3: content.locales.Locales.Create:input_type -> content.locales.CreateRequest
-	3, // 4: content.locales.Locales.List:input_type -> content.locales.ListRequest
-	5, // 5: content.locales.Locales.Delete:input_type -> content.locales.DeleteRequest
-	2, // 6: content.locales.Locales.Create:output_type -> content.locales.CreateResponse
-	4, // 7: content.locales.Locales.List:output_type -> content.locales.ListResponse
-	6, // 8: content.locales.Locales.Delete:output_type -> google.protobuf.Empty
-	6, // [6:9] is the sub-list for method output_type
-	3, // [3:6] 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
+	0, // 2: content.locales.UpdateRequest.locale:type_name -> content.locales.Locale
+	0, // 3: content.locales.ListResponse.locales:type_name -> content.locales.Locale
+	1, // 4: content.locales.Locales.Create:input_type -> content.locales.CreateRequest
+	3, // 5: content.locales.Locales.Update:input_type -> content.locales.UpdateRequest
+	4, // 6: content.locales.Locales.List:input_type -> content.locales.ListRequest
+	6, // 7: content.locales.Locales.Delete:input_type -> content.locales.DeleteRequest
+	2, // 8: content.locales.Locales.Create:output_type -> content.locales.CreateResponse
+	7, // 9: content.locales.Locales.Update:output_type -> google.protobuf.Empty
+	5, // 10: content.locales.Locales.List:output_type -> content.locales.ListResponse
+	7, // 11: content.locales.Locales.Delete:output_type -> google.protobuf.Empty
+	8, // [8:12] is the sub-list for method output_type
+	4, // [4:8] is the sub-list for method input_type
+	4, // [4:4] is the sub-list for extension type_name
+	4, // [4:4] is the sub-list for extension extendee
+	0, // [0:4] is the sub-list for field type_name
 }
 
 func init() { file_locales_locales_proto_init() }
@@ -424,7 +551,7 @@ func file_locales_locales_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_locales_locales_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_locales_locales_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Locale); i {
 			case 0:
 				return &v.state
@@ -436,7 +563,7 @@ func file_locales_locales_proto_init() {
 				return nil
 			}
 		}
-		file_locales_locales_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_locales_locales_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -448,7 +575,7 @@ func file_locales_locales_proto_init() {
 				return nil
 			}
 		}
-		file_locales_locales_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_locales_locales_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -460,7 +587,19 @@ func file_locales_locales_proto_init() {
 				return nil
 			}
 		}
-		file_locales_locales_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_locales_locales_proto_msgTypes[3].Exporter = func(v any, i int) any {
+			switch v := v.(*UpdateRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_locales_locales_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest); i {
 			case 0:
 				return &v.state
@@ -472,7 +611,7 @@ func file_locales_locales_proto_init() {
 				return nil
 			}
 		}
-		file_locales_locales_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_locales_locales_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*ListResponse); i {
 			case 0:
 				return &v.state
@@ -484,7 +623,7 @@ func file_locales_locales_proto_init() {
 				return nil
 			}
 		}
-		file_locales_locales_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_locales_locales_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -503,7 +642,7 @@ func file_locales_locales_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_locales_locales_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   6,
+			NumMessages:   7,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/proto/locales/locales_grpc.pb.go b/proto/locales/locales_grpc.pb.go
index d8f5e712b8b06e5646d56020c1e0cb06ae24545f..1e6d289f8fa50a8c1ec8a2013ac7cf5b8183d7f0 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: locales/locales.proto
 
 package locales
@@ -16,11 +16,12 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Locales_Create_FullMethodName = "/content.locales.Locales/Create"
+	Locales_Update_FullMethodName = "/content.locales.Locales/Update"
 	Locales_List_FullMethodName   = "/content.locales.Locales/List"
 	Locales_Delete_FullMethodName = "/content.locales.Locales/Delete"
 )
@@ -29,8 +30,13 @@ const (
 //
 // 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 LocalesClient interface {
+	// Создать локаль
 	Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error)
+	// Обновить локаль
+	Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
+	// Получить список локалей
 	List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
+	// Удалить локаль
 	Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
 }
 
@@ -43,8 +49,19 @@ func NewLocalesClient(cc grpc.ClientConnInterface) LocalesClient {
 }
 
 func (c *localesClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Locales_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Locales_Create_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *localesClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(emptypb.Empty)
+	err := c.cc.Invoke(ctx, Locales_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -52,8 +69,9 @@ func (c *localesClient) Create(ctx context.Context, in *CreateRequest, opts ...g
 }
 
 func (c *localesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListResponse)
-	err := c.cc.Invoke(ctx, Locales_List_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Locales_List_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -61,8 +79,9 @@ func (c *localesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.
 }
 
 func (c *localesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Locales_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Locales_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -71,21 +90,32 @@ func (c *localesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...g
 
 // LocalesServer is the server API for Locales service.
 // All implementations must embed UnimplementedLocalesServer
-// for forward compatibility
+// for forward compatibility.
 type LocalesServer interface {
+	// Создать локаль
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
+	// Обновить локаль
+	Update(context.Context, *UpdateRequest) (*emptypb.Empty, error)
+	// Получить список локалей
 	List(context.Context, *ListRequest) (*ListResponse, error)
+	// Удалить локаль
 	Delete(context.Context, *DeleteRequest) (*emptypb.Empty, error)
 	mustEmbedUnimplementedLocalesServer()
 }
 
-// UnimplementedLocalesServer must be embedded to have forward compatible implementations.
-type UnimplementedLocalesServer struct {
-}
+// UnimplementedLocalesServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedLocalesServer struct{}
 
 func (UnimplementedLocalesServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
 }
+func (UnimplementedLocalesServer) Update(context.Context, *UpdateRequest) (*emptypb.Empty, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Update not implemented")
+}
 func (UnimplementedLocalesServer) List(context.Context, *ListRequest) (*ListResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
 }
@@ -93,6 +123,7 @@ func (UnimplementedLocalesServer) Delete(context.Context, *DeleteRequest) (*empt
 	return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
 }
 func (UnimplementedLocalesServer) mustEmbedUnimplementedLocalesServer() {}
+func (UnimplementedLocalesServer) testEmbeddedByValue()                 {}
 
 // UnsafeLocalesServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to LocalesServer will
@@ -102,6 +133,13 @@ type UnsafeLocalesServer interface {
 }
 
 func RegisterLocalesServer(s grpc.ServiceRegistrar, srv LocalesServer) {
+	// If the following call pancis, it indicates UnimplementedLocalesServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Locales_ServiceDesc, srv)
 }
 
@@ -123,6 +161,24 @@ func _Locales_Create_Handler(srv interface{}, ctx context.Context, dec func(inte
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Locales_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(UpdateRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(LocalesServer).Update(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: Locales_Update_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(LocalesServer).Update(ctx, req.(*UpdateRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _Locales_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(ListRequest)
 	if err := dec(in); err != nil {
@@ -170,6 +226,10 @@ var Locales_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "Create",
 			Handler:    _Locales_Create_Handler,
 		},
+		{
+			MethodName: "Update",
+			Handler:    _Locales_Update_Handler,
+		},
 		{
 			MethodName: "List",
 			Handler:    _Locales_List_Handler,
diff --git a/proto/logs/log.pb.go b/proto/logs/log.pb.go
index 8beabf96d64b409bc99ebf0b6e6ecb64b1ae4aa6..deb2d4fc477c54e2bb1c091b29feb4b209e5b670 100644
--- a/proto/logs/log.pb.go
+++ b/proto/logs/log.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.25.1
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: logs/log.proto
 
 package logs
@@ -146,6 +146,8 @@ type LogEntry struct {
 	Attr *anypb.Any `protobuf:"bytes,10,opt,name=attr,proto3" json:"attr,omitempty"`
 	// tags содержит теги связанные с событием, на усмотрение сервиса
 	Tags []string `protobuf:"bytes,11,rep,name=tags,proto3" json:"tags,omitempty"`
+	// релевантность элемента при полнотекстовом поиске
+	SearchScore float64 `protobuf:"fixed64,12,opt,name=search_score,json=searchScore,proto3" json:"search_score,omitempty"`
 }
 
 func (x *LogEntry) Reset() {
@@ -257,6 +259,13 @@ func (x *LogEntry) GetTags() []string {
 	return nil
 }
 
+func (x *LogEntry) GetSearchScore() float64 {
+	if x != nil {
+		return x.SearchScore
+	}
+	return 0
+}
+
 var File_logs_log_proto protoreflect.FileDescriptor
 
 var file_logs_log_proto_rawDesc = []byte{
@@ -266,7 +275,7 @@ var file_logs_log_proto_rawDesc = []byte{
 	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, 0xdc, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xff, 0x02, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e,
 	0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
 	0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
 	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
@@ -288,15 +297,17 @@ var file_logs_log_proto_rawDesc = []byte{
 	0x0a, 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, 0x04, 0x61, 0x74, 0x74,
 	0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52,
-	0x04, 0x74, 0x61, 0x67, 0x73, 0x2a, 0x45, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
-	0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57,
-	0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f,
-	0x52, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10,
-	0x03, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x04, 0x42, 0x2e, 0x5a, 0x2c,
-	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, 0x6c, 0x6f, 0x67, 0x73, 0x3b, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f,
+	0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x73, 0x65, 0x61,
+	0x72, 0x63, 0x68, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x2a, 0x45, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c,
+	0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b,
+	0x0a, 0x07, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45,
+	0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43,
+	0x41, 0x4c, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x04, 0x42,
+	0x2e, 0x5a, 0x2c, 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, 0x6c, 0x6f, 0x67, 0x73, 0x3b, 0x6c, 0x6f, 0x67, 0x73, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -313,7 +324,7 @@ func file_logs_log_proto_rawDescGZIP() []byte {
 
 var file_logs_log_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_logs_log_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_logs_log_proto_goTypes = []interface{}{
+var file_logs_log_proto_goTypes = []any{
 	(LogLevel)(0),                 // 0: logs.LogLevel
 	(*LogEntry)(nil),              // 1: logs.LogEntry
 	(*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp
@@ -336,7 +347,7 @@ func file_logs_log_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_logs_log_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*LogEntry); i {
 			case 0:
 				return &v.state
diff --git a/proto/logs/log_service.pb.go b/proto/logs/log_service.pb.go
index 7036778591e3e51156c67eccffb05976fea10d38..114d5229d30504b202cd9475741b65b4d28831a3 100644
--- a/proto/logs/log_service.pb.go
+++ b/proto/logs/log_service.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.25.1
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: logs/log_service.proto
 
 package logs
@@ -568,7 +568,7 @@ func file_logs_log_service_proto_rawDescGZIP() []byte {
 }
 
 var file_logs_log_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
-var file_logs_log_service_proto_goTypes = []interface{}{
+var file_logs_log_service_proto_goTypes = []any{
 	(*LogRequest)(nil),         // 0: logs.LogRequest
 	(*LogResponse)(nil),        // 1: logs.LogResponse
 	(*Filter)(nil),             // 2: logs.Filter
@@ -613,7 +613,7 @@ func file_logs_log_service_proto_init() {
 	}
 	file_logs_log_proto_init()
 	if !protoimpl.UnsafeEnabled {
-		file_logs_log_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*LogRequest); i {
 			case 0:
 				return &v.state
@@ -625,7 +625,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*LogResponse); i {
 			case 0:
 				return &v.state
@@ -637,7 +637,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -649,7 +649,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*FindRequest); i {
 			case 0:
 				return &v.state
@@ -661,7 +661,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*FindResult); i {
 			case 0:
 				return &v.state
@@ -673,7 +673,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*FindResponse); i {
 			case 0:
 				return &v.state
@@ -685,7 +685,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -697,7 +697,7 @@ func file_logs_log_service_proto_init() {
 				return nil
 			}
 		}
-		file_logs_log_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_logs_log_service_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteResponse); i {
 			case 0:
 				return &v.state
@@ -710,7 +710,7 @@ func file_logs_log_service_proto_init() {
 			}
 		}
 	}
-	file_logs_log_service_proto_msgTypes[5].OneofWrappers = []interface{}{
+	file_logs_log_service_proto_msgTypes[5].OneofWrappers = []any{
 		(*FindResponse_Result)(nil),
 		(*FindResponse_Error)(nil),
 	}
diff --git a/proto/logs/log_service_grpc.pb.go b/proto/logs/log_service_grpc.pb.go
index ad55cee20f51d0135ab737a566f405696fe693fa..c8fd4bc4dbc5c0c044d6791f3ce62018d645a7d4 100644
--- a/proto/logs/log_service_grpc.pb.go
+++ b/proto/logs/log_service_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.25.1
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: logs/log_service.proto
 
 package logs
@@ -15,8 +15,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	LogsService_Log_FullMethodName    = "/logs.LogsService/Log"
@@ -27,6 +27,9 @@ const (
 // LogsServiceClient is the client API for LogsService 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 LogsServiceClient interface {
 	// Метод для записи логов
 	Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error)
@@ -45,8 +48,9 @@ func NewLogsServiceClient(cc grpc.ClientConnInterface) LogsServiceClient {
 }
 
 func (c *logsServiceClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(LogResponse)
-	err := c.cc.Invoke(ctx, LogsService_Log_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, LogsService_Log_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -54,8 +58,9 @@ func (c *logsServiceClient) Log(ctx context.Context, in *LogRequest, opts ...grp
 }
 
 func (c *logsServiceClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindResponse)
-	err := c.cc.Invoke(ctx, LogsService_Find_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, LogsService_Find_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -63,8 +68,9 @@ func (c *logsServiceClient) Find(ctx context.Context, in *FindRequest, opts ...g
 }
 
 func (c *logsServiceClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*DeleteResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(DeleteResponse)
-	err := c.cc.Invoke(ctx, LogsService_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, LogsService_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -73,7 +79,10 @@ func (c *logsServiceClient) Delete(ctx context.Context, in *DeleteRequest, opts
 
 // LogsServiceServer is the server API for LogsService service.
 // All implementations must embed UnimplementedLogsServiceServer
-// for forward compatibility
+// for forward compatibility.
+//
+// Сервис для записи активности в системе
+// Позволяет всем компонентам системы записывать логи в единое хранилище и получать их оттуда
 type LogsServiceServer interface {
 	// Метод для записи логов
 	Log(context.Context, *LogRequest) (*LogResponse, error)
@@ -84,9 +93,12 @@ type LogsServiceServer interface {
 	mustEmbedUnimplementedLogsServiceServer()
 }
 
-// UnimplementedLogsServiceServer must be embedded to have forward compatible implementations.
-type UnimplementedLogsServiceServer struct {
-}
+// UnimplementedLogsServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedLogsServiceServer struct{}
 
 func (UnimplementedLogsServiceServer) Log(context.Context, *LogRequest) (*LogResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Log not implemented")
@@ -98,6 +110,7 @@ func (UnimplementedLogsServiceServer) Delete(context.Context, *DeleteRequest) (*
 	return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
 }
 func (UnimplementedLogsServiceServer) mustEmbedUnimplementedLogsServiceServer() {}
+func (UnimplementedLogsServiceServer) testEmbeddedByValue()                     {}
 
 // UnsafeLogsServiceServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to LogsServiceServer will
@@ -107,6 +120,13 @@ type UnsafeLogsServiceServer interface {
 }
 
 func RegisterLogsServiceServer(s grpc.ServiceRegistrar, srv LogsServiceServer) {
+	// If the following call pancis, it indicates UnimplementedLogsServiceServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&LogsService_ServiceDesc, srv)
 }
 
diff --git a/proto/members/members.pb.go b/proto/members/members.pb.go
index a939a3f8189f6f100b61a4ad070a3c4fb30fb023..deb1ac559b3f59243f01b8ab142b57e3c98ad675 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: members/members.proto
 
 package members
@@ -759,7 +759,7 @@ func file_members_members_proto_rawDescGZIP() []byte {
 
 var file_members_members_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
 var file_members_members_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
-var file_members_members_proto_goTypes = []interface{}{
+var file_members_members_proto_goTypes = []any{
 	(Role)(0),                         // 0: account.members.Role
 	(*Member)(nil),                    // 1: account.members.Member
 	(*SetRequest)(nil),                // 2: account.members.SetRequest
@@ -807,7 +807,7 @@ func file_members_members_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_members_members_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Member); i {
 			case 0:
 				return &v.state
@@ -819,7 +819,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*SetRequest); i {
 			case 0:
 				return &v.state
@@ -831,7 +831,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -843,7 +843,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -855,7 +855,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*RemoveRequest); i {
 			case 0:
 				return &v.state
@@ -867,7 +867,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*ListMembersRequest); i {
 			case 0:
 				return &v.state
@@ -879,7 +879,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*ListMembersResponse); i {
 			case 0:
 				return &v.state
@@ -891,7 +891,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*ListOrganizationsRequest); i {
 			case 0:
 				return &v.state
@@ -903,7 +903,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*ListOrganizationsResponse); i {
 			case 0:
 				return &v.state
@@ -915,7 +915,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*OnCollaboratorSetRequest); i {
 			case 0:
 				return &v.state
@@ -927,7 +927,7 @@ func file_members_members_proto_init() {
 				return nil
 			}
 		}
-		file_members_members_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_members_members_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*OnCollaboratorSetResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/members/members_grpc.pb.go b/proto/members/members_grpc.pb.go
index c0216bfe1d7c10c12a4d02d32b5ba6774cfcd59c..8b6677f46851145550d3f509ba230997c9848e4d 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: members/members.proto
 
 package members
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Members_Set_FullMethodName               = "/account.members.Members/Set"
@@ -47,8 +47,9 @@ func NewMembersClient(cc grpc.ClientConnInterface) MembersClient {
 }
 
 func (c *membersClient) Set(ctx context.Context, in *SetRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Members_Set_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Members_Set_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -56,8 +57,9 @@ func (c *membersClient) Set(ctx context.Context, in *SetRequest, opts ...grpc.Ca
 }
 
 func (c *membersClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Members_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Members_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -65,8 +67,9 @@ func (c *membersClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Ca
 }
 
 func (c *membersClient) Remove(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Members_Remove_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Members_Remove_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -74,8 +77,9 @@ func (c *membersClient) Remove(ctx context.Context, in *RemoveRequest, opts ...g
 }
 
 func (c *membersClient) ListMembers(ctx context.Context, in *ListMembersRequest, opts ...grpc.CallOption) (*ListMembersResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListMembersResponse)
-	err := c.cc.Invoke(ctx, Members_ListMembers_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Members_ListMembers_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -83,8 +87,9 @@ func (c *membersClient) ListMembers(ctx context.Context, in *ListMembersRequest,
 }
 
 func (c *membersClient) ListOrganizations(ctx context.Context, in *ListOrganizationsRequest, opts ...grpc.CallOption) (*ListOrganizationsResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListOrganizationsResponse)
-	err := c.cc.Invoke(ctx, Members_ListOrganizations_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Members_ListOrganizations_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -93,7 +98,7 @@ func (c *membersClient) ListOrganizations(ctx context.Context, in *ListOrganizat
 
 // MembersServer is the server API for Members service.
 // All implementations must embed UnimplementedMembersServer
-// for forward compatibility
+// for forward compatibility.
 type MembersServer interface {
 	Set(context.Context, *SetRequest) (*emptypb.Empty, error)
 	Get(context.Context, *GetRequest) (*GetResponse, error)
@@ -103,9 +108,12 @@ type MembersServer interface {
 	mustEmbedUnimplementedMembersServer()
 }
 
-// UnimplementedMembersServer must be embedded to have forward compatible implementations.
-type UnimplementedMembersServer struct {
-}
+// UnimplementedMembersServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedMembersServer struct{}
 
 func (UnimplementedMembersServer) Set(context.Context, *SetRequest) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Set not implemented")
@@ -123,6 +131,7 @@ func (UnimplementedMembersServer) ListOrganizations(context.Context, *ListOrgani
 	return nil, status.Errorf(codes.Unimplemented, "method ListOrganizations not implemented")
 }
 func (UnimplementedMembersServer) mustEmbedUnimplementedMembersServer() {}
+func (UnimplementedMembersServer) testEmbeddedByValue()                 {}
 
 // UnsafeMembersServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to MembersServer will
@@ -132,6 +141,13 @@ type UnsafeMembersServer interface {
 }
 
 func RegisterMembersServer(s grpc.ServiceRegistrar, srv MembersServer) {
+	// If the following call pancis, it indicates UnimplementedMembersServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Members_ServiceDesc, srv)
 }
 
@@ -277,8 +293,9 @@ func NewObserverClient(cc grpc.ClientConnInterface) ObserverClient {
 }
 
 func (c *observerClient) OnCollaboratorSet(ctx context.Context, in *OnCollaboratorSetRequest, opts ...grpc.CallOption) (*OnCollaboratorSetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(OnCollaboratorSetResponse)
-	err := c.cc.Invoke(ctx, Observer_OnCollaboratorSet_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Observer_OnCollaboratorSet_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -287,20 +304,24 @@ func (c *observerClient) OnCollaboratorSet(ctx context.Context, in *OnCollaborat
 
 // ObserverServer is the server API for Observer service.
 // All implementations must embed UnimplementedObserverServer
-// for forward compatibility
+// for forward compatibility.
 type ObserverServer interface {
 	OnCollaboratorSet(context.Context, *OnCollaboratorSetRequest) (*OnCollaboratorSetResponse, error)
 	mustEmbedUnimplementedObserverServer()
 }
 
-// UnimplementedObserverServer must be embedded to have forward compatible implementations.
-type UnimplementedObserverServer struct {
-}
+// UnimplementedObserverServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedObserverServer struct{}
 
 func (UnimplementedObserverServer) OnCollaboratorSet(context.Context, *OnCollaboratorSetRequest) (*OnCollaboratorSetResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method OnCollaboratorSet not implemented")
 }
 func (UnimplementedObserverServer) mustEmbedUnimplementedObserverServer() {}
+func (UnimplementedObserverServer) testEmbeddedByValue()                  {}
 
 // UnsafeObserverServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to ObserverServer will
@@ -310,6 +331,13 @@ type UnsafeObserverServer interface {
 }
 
 func RegisterObserverServer(s grpc.ServiceRegistrar, srv ObserverServer) {
+	// If the following call pancis, it indicates UnimplementedObserverServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Observer_ServiceDesc, srv)
 }
 
diff --git a/proto/organizations/organizations.pb.go b/proto/organizations/organizations.pb.go
index b744640573280053557944e9cbd93a43b0be7b46..d2a220a15017de2caa99f33957d2acd000589ebd 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: organizations/organizations.proto
 
 package organizations
@@ -654,7 +654,7 @@ func file_organizations_organizations_proto_rawDescGZIP() []byte {
 }
 
 var file_organizations_organizations_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
-var file_organizations_organizations_proto_goTypes = []interface{}{
+var file_organizations_organizations_proto_goTypes = []any{
 	(*Organization)(nil),       // 0: account.organizations.Organization
 	(*CreateRequest)(nil),      // 1: account.organizations.CreateRequest
 	(*CreateResponse)(nil),     // 2: account.organizations.CreateResponse
@@ -699,7 +699,7 @@ func file_organizations_organizations_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_organizations_organizations_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Organization); i {
 			case 0:
 				return &v.state
@@ -711,7 +711,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -723,7 +723,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -735,7 +735,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -747,7 +747,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -759,7 +759,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -771,7 +771,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -783,7 +783,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -795,7 +795,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*FindRequest); i {
 			case 0:
 				return &v.state
@@ -807,7 +807,7 @@ func file_organizations_organizations_proto_init() {
 				return nil
 			}
 		}
-		file_organizations_organizations_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_organizations_organizations_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*FindResponse); i {
 			case 0:
 				return &v.state
@@ -820,7 +820,7 @@ func file_organizations_organizations_proto_init() {
 			}
 		}
 	}
-	file_organizations_organizations_proto_msgTypes[0].OneofWrappers = []interface{}{}
+	file_organizations_organizations_proto_msgTypes[0].OneofWrappers = []any{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
diff --git a/proto/organizations/organizations_grpc.pb.go b/proto/organizations/organizations_grpc.pb.go
index dc737ac9a2eb95cb6c977f537216942dfd4164d7..09aa7525515d4688ae40eed6aa7d0107feb5ad62 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: organizations/organizations.proto
 
 package organizations
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Organizations_Create_FullMethodName = "/account.organizations.Organizations/Create"
@@ -47,8 +47,9 @@ func NewOrganizationsClient(cc grpc.ClientConnInterface) OrganizationsClient {
 }
 
 func (c *organizationsClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Organizations_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Organizations_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -56,8 +57,9 @@ func (c *organizationsClient) Create(ctx context.Context, in *CreateRequest, opt
 }
 
 func (c *organizationsClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Organizations_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Organizations_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -65,8 +67,9 @@ func (c *organizationsClient) Get(ctx context.Context, in *GetRequest, opts ...g
 }
 
 func (c *organizationsClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Organizations_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Organizations_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -74,8 +77,9 @@ func (c *organizationsClient) Delete(ctx context.Context, in *DeleteRequest, opt
 }
 
 func (c *organizationsClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindResponse)
-	err := c.cc.Invoke(ctx, Organizations_Find_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Organizations_Find_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -83,8 +87,9 @@ func (c *organizationsClient) Find(ctx context.Context, in *FindRequest, opts ..
 }
 
 func (c *organizationsClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Organizations_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Organizations_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -93,7 +98,7 @@ func (c *organizationsClient) Update(ctx context.Context, in *UpdateRequest, opt
 
 // OrganizationsServer is the server API for Organizations service.
 // All implementations must embed UnimplementedOrganizationsServer
-// for forward compatibility
+// for forward compatibility.
 type OrganizationsServer interface {
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
 	Get(context.Context, *GetRequest) (*GetResponse, error)
@@ -103,9 +108,12 @@ type OrganizationsServer interface {
 	mustEmbedUnimplementedOrganizationsServer()
 }
 
-// UnimplementedOrganizationsServer must be embedded to have forward compatible implementations.
-type UnimplementedOrganizationsServer struct {
-}
+// UnimplementedOrganizationsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedOrganizationsServer struct{}
 
 func (UnimplementedOrganizationsServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -123,6 +131,7 @@ func (UnimplementedOrganizationsServer) Update(context.Context, *UpdateRequest)
 	return nil, status.Errorf(codes.Unimplemented, "method Update not implemented")
 }
 func (UnimplementedOrganizationsServer) mustEmbedUnimplementedOrganizationsServer() {}
+func (UnimplementedOrganizationsServer) testEmbeddedByValue()                       {}
 
 // UnsafeOrganizationsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to OrganizationsServer will
@@ -132,6 +141,13 @@ type UnsafeOrganizationsServer interface {
 }
 
 func RegisterOrganizationsServer(s grpc.ServiceRegistrar, srv OrganizationsServer) {
+	// If the following call pancis, it indicates UnimplementedOrganizationsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Organizations_ServiceDesc, srv)
 }
 
diff --git a/proto/references/references.pb.go b/proto/references/references.pb.go
index 64895f9fa4b2e475dbc9444ee9998fc3d4204699..235312d912171d073957187c4087c563c010a58c 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.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: references/references.proto
 
 package references
@@ -92,6 +92,7 @@ type GetRequest struct {
 	SpaceId    string       `protobuf:"bytes,1,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
 	EnvId      string       `protobuf:"bytes,2,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
 	References []*Reference `protobuf:"bytes,3,rep,name=references,proto3" json:"references,omitempty"`
+	Options    *GetOptions  `protobuf:"bytes,4,opt,name=options,proto3" json:"options,omitempty"` // Дополнительные параметры поиска
 }
 
 func (x *GetRequest) Reset() {
@@ -147,6 +148,13 @@ func (x *GetRequest) GetReferences() []*Reference {
 	return nil
 }
 
+func (x *GetRequest) GetOptions() *GetOptions {
+	if x != nil {
+		return x.Options
+	}
+	return nil
+}
+
 type GetResponse struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -346,6 +354,61 @@ func (x *PublishResponse) GetUnpublished() []*Reference {
 	return nil
 }
 
+type GetOptions struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	LocaleId        string   `protobuf:"bytes,1,opt,name=locale_id,json=localeId,proto3" json:"locale_id,omitempty"`                      // Язык перевода который будет использоваться. Если не указан, то возвращаются данные для языка по умолчанию
+	TranslationsIds []string `protobuf:"bytes,2,rep,name=translations_ids,json=translationsIds,proto3" json:"translations_ids,omitempty"` // Список идентификаторов переводов/локалей, которых должны быть включены в результат
+}
+
+func (x *GetOptions) Reset() {
+	*x = GetOptions{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_references_references_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetOptions) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetOptions) ProtoMessage() {}
+
+func (x *GetOptions) ProtoReflect() protoreflect.Message {
+	mi := &file_references_references_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 GetOptions.ProtoReflect.Descriptor instead.
+func (*GetOptions) Descriptor() ([]byte, []int) {
+	return file_references_references_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *GetOptions) GetLocaleId() string {
+	if x != nil {
+		return x.LocaleId
+	}
+	return ""
+}
+
+func (x *GetOptions) GetTranslationsIds() []string {
+	if x != nil {
+		return x.TranslationsIds
+	}
+	return nil
+}
+
 var File_references_references_proto protoreflect.FileDescriptor
 
 var file_references_references_proto_rawDesc = []byte{
@@ -359,61 +422,70 @@ var file_references_references_proto_rawDesc = []byte{
 	0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
 	0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
 	0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
-	0x65, 0x64, 0x22, 0x7d, 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, 0x3d, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73,
-	0x18, 0x03, 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, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
-	0x73, 0x22, 0x73, 0x0a, 0x0b, 0x47, 0x65, 0x74, 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, 0x39, 0x0a, 0x08, 0x6e,
-	0x6f, 0x74, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 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, 0x08, 0x6e, 0x6f,
-	0x74, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0xb5, 0x01, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69,
-	0x73, 0x68, 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, 0x3d, 0x0a, 0x0a, 0x72,
-	0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x03, 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, 0x0a,
-	0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65,
-	0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72,
-	0x65, 0x63, 0x75, 0x72, 0x73, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63,
-	0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xca,
-	0x01, 0x0a, 0x0f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18,
-	0x02, 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, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12,
-	0x39, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x03, 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, 0x08, 0x6e, 0x6f, 0x74, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x0b, 0x75, 0x6e,
-	0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x04, 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, 0x0b,
-	0x75, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x32, 0xac, 0x01, 0x0a, 0x0a,
-	0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x03, 0x47, 0x65,
-	0x74, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65,
-	0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65,
-	0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12,
-	0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
-	0x6e, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65,
-	0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
-	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, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x3b, 0x72, 0x65, 0x66, 0x65,
-	0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x64, 0x22, 0xb7, 0x01, 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, 0x3d, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+	0x73, 0x18, 0x03, 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, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+	0x65, 0x73, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65,
+	0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x73, 0x0a, 0x0b,
+	0x47, 0x65, 0x74, 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, 0x39, 0x0a, 0x08, 0x6e, 0x6f, 0x74, 0x66, 0x6f, 0x75,
+	0x6e, 0x64, 0x18, 0x02, 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, 0x08, 0x6e, 0x6f, 0x74, 0x66, 0x6f, 0x75, 0x6e,
+	0x64, 0x22, 0xb5, 0x01, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 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, 0x3d, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+	0x6e, 0x63, 0x65, 0x73, 0x18, 0x03, 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, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72,
+	0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73, 0x69,
+	0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73,
+	0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x0f, 0x50, 0x75,
+	0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a,
+	0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x02, 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,
+	0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x08, 0x6e, 0x6f,
+	0x74, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x03, 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, 0x08, 0x6e, 0x6f, 0x74,
+	0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x3f, 0x0a, 0x0b, 0x75, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69,
+	0x73, 0x68, 0x65, 0x64, 0x18, 0x04, 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, 0x0b, 0x75, 0x6e, 0x70, 0x75, 0x62,
+	0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x22, 0x54, 0x0a, 0x0a, 0x47, 0x65, 0x74, 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, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61,
+	0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x64, 0x73, 0x32, 0xac, 0x01, 0x0a,
+	0x0a, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x03, 0x47,
+	0x65, 0x74, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66,
+	0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66,
+	0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x54, 0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
+	0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65, 0x72,
+	0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72,
+	0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
+	0x68, 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, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x3b, 0x72, 0x65, 0x66,
+	0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -428,32 +500,34 @@ func file_references_references_proto_rawDescGZIP() []byte {
 	return file_references_references_proto_rawDescData
 }
 
-var file_references_references_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
-var file_references_references_proto_goTypes = []interface{}{
+var file_references_references_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
+var file_references_references_proto_goTypes = []any{
 	(*Reference)(nil),       // 0: content.references.Reference
 	(*GetRequest)(nil),      // 1: content.references.GetRequest
 	(*GetResponse)(nil),     // 2: content.references.GetResponse
 	(*PublishRequest)(nil),  // 3: content.references.PublishRequest
 	(*PublishResponse)(nil), // 4: content.references.PublishResponse
-	(*items.Item)(nil),      // 5: content.items.Item
+	(*GetOptions)(nil),      // 5: content.references.GetOptions
+	(*items.Item)(nil),      // 6: content.items.Item
 }
 var file_references_references_proto_depIdxs = []int32{
-	0, // 0: content.references.GetRequest.references:type_name -> content.references.Reference
-	5, // 1: content.references.GetResponse.items:type_name -> content.items.Item
-	0, // 2: content.references.GetResponse.notfound:type_name -> content.references.Reference
-	0, // 3: content.references.PublishRequest.references:type_name -> content.references.Reference
-	0, // 4: content.references.PublishResponse.published:type_name -> content.references.Reference
-	0, // 5: content.references.PublishResponse.notfound:type_name -> content.references.Reference
-	0, // 6: content.references.PublishResponse.unpublished:type_name -> content.references.Reference
-	1, // 7: content.references.References.Get:input_type -> content.references.GetRequest
-	3, // 8: content.references.References.Publish:input_type -> content.references.PublishRequest
-	2, // 9: content.references.References.Get:output_type -> content.references.GetResponse
-	4, // 10: content.references.References.Publish:output_type -> content.references.PublishResponse
-	9, // [9:11] is the sub-list for method output_type
-	7, // [7:9] is the sub-list for method input_type
-	7, // [7:7] is the sub-list for extension type_name
-	7, // [7:7] is the sub-list for extension extendee
-	0, // [0:7] is the sub-list for field type_name
+	0,  // 0: content.references.GetRequest.references:type_name -> content.references.Reference
+	5,  // 1: content.references.GetRequest.options:type_name -> content.references.GetOptions
+	6,  // 2: content.references.GetResponse.items:type_name -> content.items.Item
+	0,  // 3: content.references.GetResponse.notfound:type_name -> content.references.Reference
+	0,  // 4: content.references.PublishRequest.references:type_name -> content.references.Reference
+	0,  // 5: content.references.PublishResponse.published:type_name -> content.references.Reference
+	0,  // 6: content.references.PublishResponse.notfound:type_name -> content.references.Reference
+	0,  // 7: content.references.PublishResponse.unpublished:type_name -> content.references.Reference
+	1,  // 8: content.references.References.Get:input_type -> content.references.GetRequest
+	3,  // 9: content.references.References.Publish:input_type -> content.references.PublishRequest
+	2,  // 10: content.references.References.Get:output_type -> content.references.GetResponse
+	4,  // 11: content.references.References.Publish:output_type -> content.references.PublishResponse
+	10, // [10:12] is the sub-list for method output_type
+	8,  // [8:10] 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_references_references_proto_init() }
@@ -462,7 +536,7 @@ func file_references_references_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_references_references_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_references_references_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Reference); i {
 			case 0:
 				return &v.state
@@ -474,7 +548,7 @@ func file_references_references_proto_init() {
 				return nil
 			}
 		}
-		file_references_references_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_references_references_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -486,7 +560,7 @@ func file_references_references_proto_init() {
 				return nil
 			}
 		}
-		file_references_references_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_references_references_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -498,7 +572,7 @@ func file_references_references_proto_init() {
 				return nil
 			}
 		}
-		file_references_references_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_references_references_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*PublishRequest); i {
 			case 0:
 				return &v.state
@@ -510,7 +584,7 @@ func file_references_references_proto_init() {
 				return nil
 			}
 		}
-		file_references_references_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_references_references_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*PublishResponse); i {
 			case 0:
 				return &v.state
@@ -522,6 +596,18 @@ func file_references_references_proto_init() {
 				return nil
 			}
 		}
+		file_references_references_proto_msgTypes[5].Exporter = func(v any, i int) any {
+			switch v := v.(*GetOptions); 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{
@@ -529,7 +615,7 @@ func file_references_references_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_references_references_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   5,
+			NumMessages:   6,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/proto/references/references_grpc.pb.go b/proto/references/references_grpc.pb.go
index 750571cede9b697ed3392cd96941b2db54934fcb..f6b37c167ea54f950198a969a7c2f3420346d5c8 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.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: references/references.proto
 
 package references
@@ -15,8 +15,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	References_Get_FullMethodName     = "/content.references.References/Get"
@@ -26,6 +26,8 @@ const (
 // ReferencesClient is the client API for References 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.
+//
+// References сервис предназначен для работы со ссылками на записи
 type ReferencesClient interface {
 	// Get возвращает список записей по ссылкам
 	Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
@@ -51,8 +53,9 @@ func NewReferencesClient(cc grpc.ClientConnInterface) ReferencesClient {
 }
 
 func (c *referencesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, References_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, References_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -60,8 +63,9 @@ func (c *referencesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc
 }
 
 func (c *referencesClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(PublishResponse)
-	err := c.cc.Invoke(ctx, References_Publish_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, References_Publish_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -70,7 +74,9 @@ func (c *referencesClient) Publish(ctx context.Context, in *PublishRequest, opts
 
 // ReferencesServer is the server API for References service.
 // All implementations must embed UnimplementedReferencesServer
-// for forward compatibility
+// for forward compatibility.
+//
+// References сервис предназначен для работы со ссылками на записи
 type ReferencesServer interface {
 	// Get возвращает список записей по ссылкам
 	Get(context.Context, *GetRequest) (*GetResponse, error)
@@ -88,9 +94,12 @@ type ReferencesServer interface {
 	mustEmbedUnimplementedReferencesServer()
 }
 
-// UnimplementedReferencesServer must be embedded to have forward compatible implementations.
-type UnimplementedReferencesServer struct {
-}
+// UnimplementedReferencesServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedReferencesServer struct{}
 
 func (UnimplementedReferencesServer) Get(context.Context, *GetRequest) (*GetResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
@@ -99,6 +108,7 @@ func (UnimplementedReferencesServer) Publish(context.Context, *PublishRequest) (
 	return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented")
 }
 func (UnimplementedReferencesServer) mustEmbedUnimplementedReferencesServer() {}
+func (UnimplementedReferencesServer) testEmbeddedByValue()                    {}
 
 // UnsafeReferencesServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to ReferencesServer will
@@ -108,6 +118,13 @@ type UnsafeReferencesServer interface {
 }
 
 func RegisterReferencesServer(s grpc.ServiceRegistrar, srv ReferencesServer) {
+	// If the following call pancis, it indicates UnimplementedReferencesServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&References_ServiceDesc, srv)
 }
 
diff --git a/proto/roles/roles.pb.go b/proto/roles/roles.pb.go
index 0d70814977a6b765b8377739bd3f43704b98ec02..380ce70fad2a498d1349c019238cb26086131c1f 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: roles/roles.proto
 
 package roles
@@ -605,7 +605,7 @@ func file_roles_roles_proto_rawDescGZIP() []byte {
 }
 
 var file_roles_roles_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
-var file_roles_roles_proto_goTypes = []interface{}{
+var file_roles_roles_proto_goTypes = []any{
 	(*Role)(nil),           // 0: content.roles.Role
 	(*CreateRequest)(nil),  // 1: content.roles.CreateRequest
 	(*CreateResponse)(nil), // 2: content.roles.CreateResponse
@@ -648,7 +648,7 @@ func file_roles_roles_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_roles_roles_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Role); i {
 			case 0:
 				return &v.state
@@ -660,7 +660,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -672,7 +672,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -684,7 +684,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -696,7 +696,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -708,7 +708,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -720,7 +720,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest); i {
 			case 0:
 				return &v.state
@@ -732,7 +732,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*ListResponse); i {
 			case 0:
 				return &v.state
@@ -744,7 +744,7 @@ func file_roles_roles_proto_init() {
 				return nil
 			}
 		}
-		file_roles_roles_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_roles_roles_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
diff --git a/proto/roles/roles_grpc.pb.go b/proto/roles/roles_grpc.pb.go
index 13000eb2e08429aa6fcc93d5e133d4e3012c05ff..e82284338b495f0fc5ab7c67f419a0ca22e3484b 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: roles/roles.proto
 
 package roles
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Roles_Create_FullMethodName = "/content.roles.Roles/Create"
@@ -52,8 +52,9 @@ func NewRolesClient(cc grpc.ClientConnInterface) RolesClient {
 }
 
 func (c *rolesClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Roles_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Roles_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -61,8 +62,9 @@ func (c *rolesClient) Create(ctx context.Context, in *CreateRequest, opts ...grp
 }
 
 func (c *rolesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Roles_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Roles_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -70,8 +72,9 @@ func (c *rolesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Call
 }
 
 func (c *rolesClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Roles_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Roles_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -79,8 +82,9 @@ func (c *rolesClient) Update(ctx context.Context, in *UpdateRequest, opts ...grp
 }
 
 func (c *rolesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListResponse)
-	err := c.cc.Invoke(ctx, Roles_List_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Roles_List_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -88,8 +92,9 @@ func (c *rolesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.Ca
 }
 
 func (c *rolesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Roles_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Roles_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -98,7 +103,7 @@ func (c *rolesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grp
 
 // RolesServer is the server API for Roles service.
 // All implementations must embed UnimplementedRolesServer
-// for forward compatibility
+// for forward compatibility.
 type RolesServer interface {
 	// Create - создает роль в рамках пространства
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
@@ -113,9 +118,12 @@ type RolesServer interface {
 	mustEmbedUnimplementedRolesServer()
 }
 
-// UnimplementedRolesServer must be embedded to have forward compatible implementations.
-type UnimplementedRolesServer struct {
-}
+// UnimplementedRolesServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedRolesServer struct{}
 
 func (UnimplementedRolesServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -133,6 +141,7 @@ func (UnimplementedRolesServer) Delete(context.Context, *DeleteRequest) (*emptyp
 	return nil, status.Errorf(codes.Unimplemented, "method Delete not implemented")
 }
 func (UnimplementedRolesServer) mustEmbedUnimplementedRolesServer() {}
+func (UnimplementedRolesServer) testEmbeddedByValue()               {}
 
 // UnsafeRolesServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to RolesServer will
@@ -142,6 +151,13 @@ type UnsafeRolesServer interface {
 }
 
 func RegisterRolesServer(s grpc.ServiceRegistrar, srv RolesServer) {
+	// If the following call pancis, it indicates UnimplementedRolesServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Roles_ServiceDesc, srv)
 }
 
diff --git a/proto/spaces/spaces.pb.go b/proto/spaces/spaces.pb.go
index aedef2c39158d62f2fb08844c27c9c7f0fac6cb1..32a78ef8be7bf42af39daa7b3199311efe483c55 100644
--- a/proto/spaces/spaces.pb.go
+++ b/proto/spaces/spaces.pb.go
@@ -1,12 +1,13 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.31.0
-// 	protoc        v4.25.1
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: spaces/spaces.proto
 
 package spaces
 
 import (
+	common "git.perx.ru/perxis/perxis-go/proto/common"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	emptypb "google.golang.org/protobuf/types/known/emptypb"
@@ -991,6 +992,197 @@ func (x *MoveRequest) GetOrgId() string {
 	return ""
 }
 
+type Filter struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id            []string `protobuf:"bytes,1,rep,name=id,proto3" json:"id,omitempty"`                                              // Список ID пространств
+	OrgId         []string `protobuf:"bytes,2,rep,name=org_id,json=orgId,proto3" json:"org_id,omitempty"`                           // Список организаций
+	Name          []string `protobuf:"bytes,3,rep,name=name,proto3" json:"name,omitempty"`                                          // Список названий
+	State         []State  `protobuf:"varint,4,rep,packed,name=state,proto3,enum=content.spaces.State" json:"state,omitempty"`      // Список состояний
+	TransferToOrg []string `protobuf:"bytes,5,rep,name=transfer_to_org,json=transferToOrg,proto3" json:"transfer_to_org,omitempty"` // Список организаций, в которые запрошен перенос пространства
+}
+
+func (x *Filter) Reset() {
+	*x = Filter{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_spaces_spaces_proto_msgTypes[17]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Filter) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Filter) ProtoMessage() {}
+
+func (x *Filter) ProtoReflect() protoreflect.Message {
+	mi := &file_spaces_spaces_proto_msgTypes[17]
+	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 Filter.ProtoReflect.Descriptor instead.
+func (*Filter) Descriptor() ([]byte, []int) {
+	return file_spaces_spaces_proto_rawDescGZIP(), []int{17}
+}
+
+func (x *Filter) GetId() []string {
+	if x != nil {
+		return x.Id
+	}
+	return nil
+}
+
+func (x *Filter) GetOrgId() []string {
+	if x != nil {
+		return x.OrgId
+	}
+	return nil
+}
+
+func (x *Filter) GetName() []string {
+	if x != nil {
+		return x.Name
+	}
+	return nil
+}
+
+func (x *Filter) GetState() []State {
+	if x != nil {
+		return x.State
+	}
+	return nil
+}
+
+func (x *Filter) GetTransferToOrg() []string {
+	if x != nil {
+		return x.TransferToOrg
+	}
+	return nil
+}
+
+type FindRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Фильтры для поиска
+	Filter *Filter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"`
+	// Опции поиска
+	Options *common.FindOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"`
+}
+
+func (x *FindRequest) Reset() {
+	*x = FindRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_spaces_spaces_proto_msgTypes[18]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FindRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FindRequest) ProtoMessage() {}
+
+func (x *FindRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_spaces_spaces_proto_msgTypes[18]
+	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 FindRequest.ProtoReflect.Descriptor instead.
+func (*FindRequest) Descriptor() ([]byte, []int) {
+	return file_spaces_spaces_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *FindRequest) GetFilter() *Filter {
+	if x != nil {
+		return x.Filter
+	}
+	return nil
+}
+
+func (x *FindRequest) GetOptions() *common.FindOptions {
+	if x != nil {
+		return x.Options
+	}
+	return nil
+}
+
+type FindResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Spaces []*Space `protobuf:"bytes,1,rep,name=spaces,proto3" json:"spaces,omitempty"`
+	Total  int32    `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"`
+}
+
+func (x *FindResponse) Reset() {
+	*x = FindResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_spaces_spaces_proto_msgTypes[19]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FindResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FindResponse) ProtoMessage() {}
+
+func (x *FindResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_spaces_spaces_proto_msgTypes[19]
+	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 FindResponse.ProtoReflect.Descriptor instead.
+func (*FindResponse) Descriptor() ([]byte, []int) {
+	return file_spaces_spaces_proto_rawDescGZIP(), []int{19}
+}
+
+func (x *FindResponse) GetSpaces() []*Space {
+	if x != nil {
+		return x.Spaces
+	}
+	return nil
+}
+
+func (x *FindResponse) GetTotal() int32 {
+	if x != nil {
+		return x.Total
+	}
+	return 0
+}
+
 var File_spaces_spaces_proto protoreflect.FileDescriptor
 
 var file_spaces_spaces_proto_rawDesc = []byte{
@@ -1000,148 +1192,176 @@ var file_spaces_spaces_proto_rawDesc = []byte{
 	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 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, 0x22, 0xa3, 0x02, 0x0a, 0x05, 0x53, 0x70, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a,
-	0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a,
-	0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f,
-	0x72, 0x67, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
-	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74,
-	0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65,
-	0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73,
-	0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x72, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x6f, 0x4f, 0x72, 0x67, 0x12,
-	0x2e, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73,
-	0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
-	0x38, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09,
-	0x73, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x9b, 0x01, 0x0a, 0x09, 0x53, 0x74,
-	0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73,
-	0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x62, 0x5f, 0x76,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x62,
-	0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18,
-	0x04, 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, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x24, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0x3c, 0x0a,
-	0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b,
+	0x6f, 0x74, 0x6f, 0x1a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
+	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x02, 0x0a, 0x05, 0x53, 0x70, 0x61,
+	0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
+	0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a,
+	0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+	0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e,
+	0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0f,
+	0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x72, 0x67, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54,
+	0x6f, 0x4f, 0x72, 0x67, 0x12, 0x2e, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0a,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73,
+	0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e,
+	0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49,
+	0x6e, 0x66, 0x6f, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x9b,
+	0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2b, 0x0a, 0x05,
+	0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66,
+	0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a,
+	0x0a, 0x64, 0x62, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x05, 0x52, 0x09, 0x64, 0x62, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04,
+	0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 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, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x24, 0x0a, 0x06,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72,
+	0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72,
+	0x65, 0x73, 0x22, 0x3c, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61,
+	0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x22, 0x41, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61,
+	0x74, 0x65, 0x64, 0x22, 0x27, 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, 0x22, 0x3a, 0x0a, 0x0b,
+	0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x73,
+	0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63,
+	0x65, 0x52, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x24, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, 0x3d,
+	0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d,
+	0x0a, 0x06, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e,
+	0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x06, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x3c, 0x0a,
+	0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b,
 	0x0a, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e,
 	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53,
-	0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x41, 0x0a, 0x0e, 0x43,
-	0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a,
-	0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15,
-	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e,
-	0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x27,
-	0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08,
+	0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x60, 0x0a, 0x13, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x2e, 0x0a,
+	0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x2a, 0x0a,
+	0x0d, 0x44, 0x65, 0x6c, 0x65, 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, 0x22, 0x54, 0x0a, 0x0f, 0x54, 0x72, 0x61,
+	0x6e, 0x73, 0x66, 0x65, 0x72, 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, 0x22, 0x3a, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x22, 0x24, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, 0x3d, 0x0a, 0x0c, 0x4c, 0x69, 0x73,
-	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65,
-	0x52, 0x06, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x3c, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52,
-	0x05, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x60, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
-	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x2e, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x2a, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65,
-	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, 0x22, 0x54, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
+	0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73,
+	0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x72, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x6f, 0x4f, 0x72, 0x67, 0x22,
+	0x31, 0x0a, 0x14, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
 	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, 0x26, 0x0a, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74,
-	0x6f, 0x5f, 0x6f, 0x72, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61,
-	0x6e, 0x73, 0x66, 0x65, 0x72, 0x54, 0x6f, 0x4f, 0x72, 0x67, 0x22, 0x31, 0x0a, 0x14, 0x41, 0x62,
-	0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 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, 0x22, 0x2d, 0x0a,
-	0x14, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, 0x46, 0x0a, 0x15,
-	0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x06, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x73, 0x22, 0x3f, 0x0a, 0x0b, 0x4d, 0x6f, 0x76, 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, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
-	0x6f, 0x72, 0x67, 0x49, 0x64, 0x2a, 0x70, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b,
-	0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4e,
-	0x45, 0x57, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x02, 0x12,
-	0x0d, 0x0a, 0x09, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0f,
-	0x0a, 0x0b, 0x4d, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x10, 0x04, 0x12,
-	0x0d, 0x0a, 0x09, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x12, 0x0c,
-	0x0a, 0x08, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05,
-	0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x07, 0x32, 0xe6, 0x05, 0x0a, 0x06, 0x53, 0x70, 0x61, 0x63,
-	0x65, 0x73, 0x12, 0x49, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x63,
-	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72,
-	0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x43, 0x72, 0x65,
-	0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a,
-	0x03, 0x47, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65,
-	0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
-	0x43, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
-	0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1d,
+	0x49, 0x64, 0x22, 0x2d, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66,
+	0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6f, 0x72,
+	0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49,
+	0x64, 0x22, 0x46, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65,
+	0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63,
+	0x65, 0x52, 0x06, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x22, 0x3f, 0x0a, 0x0b, 0x4d, 0x6f, 0x76,
+	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, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x22, 0x98, 0x01, 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, 0x15, 0x0a, 0x06, 0x6f, 0x72, 0x67, 0x5f, 0x69, 0x64, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x2b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0e, 0x32,
+	0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73,
+	0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a,
+	0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x6f, 0x72, 0x67,
+	0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
+	0x54, 0x6f, 0x4f, 0x72, 0x67, 0x22, 0x6c, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73,
+	0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69,
+	0x6c, 0x74, 0x65, 0x72, 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, 0x53, 0x0a, 0x0c, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x52, 0x06, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x2a, 0x70, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74,
+	0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07,
+	0x0a, 0x03, 0x4e, 0x45, 0x57, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x41, 0x44, 0x59,
+	0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x49, 0x4e, 0x47, 0x10,
+	0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x41, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45,
+	0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x4d, 0x49, 0x47, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10,
+	0x05, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12,
+	0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x07, 0x32, 0xab, 0x06, 0x0a, 0x06, 0x53,
+	0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12,
+	0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73,
+	0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
 	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x4d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74,
-	0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
-	0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x41, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
-	0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65,
-	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, 0x45, 0x0a, 0x08, 0x54, 0x72, 0x61,
-	0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 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, 0x4f, 0x0a, 0x0d, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65,
-	0x72, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63,
-	0x65, 0x73, 0x2e, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
+	0x12, 0x40, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x22, 0x00, 0x12, 0x43, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x04, 0x46, 0x69, 0x6e, 0x64, 0x12,
+	0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73,
+	0x2e, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x63,
+	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x46, 0x69,
+	0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x06,
+	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 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,
+	0x4d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73,
+	0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x41,
+	0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x5e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65,
-	0x72, 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
-	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72,
-	0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x00, 0x12, 0x3d, 0x0a, 0x04, 0x4d, 0x6f, 0x76, 0x65, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 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, 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, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x00, 0x12, 0x45, 0x0a, 0x08, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x54,
+	0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 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, 0x4f, 0x0a, 0x0d, 0x41, 0x62, 0x6f, 0x72,
+	0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x41, 0x62, 0x6f, 0x72, 0x74,
+	0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 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, 0x5e, 0x0a, 0x0d, 0x4c, 0x69, 0x73,
+	0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74,
+	0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x73, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x04, 0x4d, 0x6f, 0x76,
+	0x65, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x73, 0x2e, 0x4d, 0x6f, 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, 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, 0x73,
+	0x70, 0x61, 0x63, 0x65, 0x73, 0x3b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1157,8 +1377,8 @@ func file_spaces_spaces_proto_rawDescGZIP() []byte {
 }
 
 var file_spaces_spaces_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_spaces_spaces_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
-var file_spaces_spaces_proto_goTypes = []interface{}{
+var file_spaces_spaces_proto_msgTypes = make([]protoimpl.MessageInfo, 20)
+var file_spaces_spaces_proto_goTypes = []any{
 	(State)(0),                    // 0: content.spaces.State
 	(*Space)(nil),                 // 1: content.spaces.Space
 	(*StateInfo)(nil),             // 2: content.spaces.StateInfo
@@ -1177,15 +1397,19 @@ var file_spaces_spaces_proto_goTypes = []interface{}{
 	(*ListTransfersRequest)(nil),  // 15: content.spaces.ListTransfersRequest
 	(*ListTransfersResponse)(nil), // 16: content.spaces.ListTransfersResponse
 	(*MoveRequest)(nil),           // 17: content.spaces.MoveRequest
-	(*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
-	(*emptypb.Empty)(nil),         // 19: google.protobuf.Empty
+	(*Filter)(nil),                // 18: content.spaces.Filter
+	(*FindRequest)(nil),           // 19: content.spaces.FindRequest
+	(*FindResponse)(nil),          // 20: content.spaces.FindResponse
+	(*timestamppb.Timestamp)(nil), // 21: google.protobuf.Timestamp
+	(*common.FindOptions)(nil),    // 22: common.FindOptions
+	(*emptypb.Empty)(nil),         // 23: google.protobuf.Empty
 }
 var file_spaces_spaces_proto_depIdxs = []int32{
 	0,  // 0: content.spaces.Space.state:type_name -> content.spaces.State
 	3,  // 1: content.spaces.Space.config:type_name -> content.spaces.Config
 	2,  // 2: content.spaces.Space.state_info:type_name -> content.spaces.StateInfo
 	0,  // 3: content.spaces.StateInfo.state:type_name -> content.spaces.State
-	18, // 4: content.spaces.StateInfo.time:type_name -> google.protobuf.Timestamp
+	21, // 4: content.spaces.StateInfo.time:type_name -> google.protobuf.Timestamp
 	1,  // 5: content.spaces.CreateRequest.space:type_name -> content.spaces.Space
 	1,  // 6: content.spaces.CreateResponse.created:type_name -> content.spaces.Space
 	1,  // 7: content.spaces.GetResponse.space:type_name -> content.spaces.Space
@@ -1193,31 +1417,37 @@ var file_spaces_spaces_proto_depIdxs = []int32{
 	1,  // 9: content.spaces.UpdateRequest.space:type_name -> content.spaces.Space
 	3,  // 10: content.spaces.UpdateConfigRequest.config:type_name -> content.spaces.Config
 	1,  // 11: content.spaces.ListTransfersResponse.spaces:type_name -> content.spaces.Space
-	4,  // 12: content.spaces.Spaces.Create:input_type -> content.spaces.CreateRequest
-	6,  // 13: content.spaces.Spaces.Get:input_type -> content.spaces.GetRequest
-	8,  // 14: content.spaces.Spaces.List:input_type -> content.spaces.ListRequest
-	10, // 15: content.spaces.Spaces.Update:input_type -> content.spaces.UpdateRequest
-	11, // 16: content.spaces.Spaces.UpdateConfig:input_type -> content.spaces.UpdateConfigRequest
-	12, // 17: content.spaces.Spaces.Delete:input_type -> content.spaces.DeleteRequest
-	13, // 18: content.spaces.Spaces.Transfer:input_type -> content.spaces.TransferRequest
-	14, // 19: content.spaces.Spaces.AbortTransfer:input_type -> content.spaces.AbortTransferRequest
-	15, // 20: content.spaces.Spaces.ListTransfers:input_type -> content.spaces.ListTransfersRequest
-	17, // 21: content.spaces.Spaces.Move:input_type -> content.spaces.MoveRequest
-	5,  // 22: content.spaces.Spaces.Create:output_type -> content.spaces.CreateResponse
-	7,  // 23: content.spaces.Spaces.Get:output_type -> content.spaces.GetResponse
-	9,  // 24: content.spaces.Spaces.List:output_type -> content.spaces.ListResponse
-	19, // 25: content.spaces.Spaces.Update:output_type -> google.protobuf.Empty
-	19, // 26: content.spaces.Spaces.UpdateConfig:output_type -> google.protobuf.Empty
-	19, // 27: content.spaces.Spaces.Delete:output_type -> google.protobuf.Empty
-	19, // 28: content.spaces.Spaces.Transfer:output_type -> google.protobuf.Empty
-	19, // 29: content.spaces.Spaces.AbortTransfer:output_type -> google.protobuf.Empty
-	16, // 30: content.spaces.Spaces.ListTransfers:output_type -> content.spaces.ListTransfersResponse
-	19, // 31: content.spaces.Spaces.Move:output_type -> google.protobuf.Empty
-	22, // [22:32] is the sub-list for method output_type
-	12, // [12:22] is the sub-list for method input_type
-	12, // [12:12] is the sub-list for extension type_name
-	12, // [12:12] is the sub-list for extension extendee
-	0,  // [0:12] is the sub-list for field type_name
+	0,  // 12: content.spaces.Filter.state:type_name -> content.spaces.State
+	18, // 13: content.spaces.FindRequest.filter:type_name -> content.spaces.Filter
+	22, // 14: content.spaces.FindRequest.options:type_name -> common.FindOptions
+	1,  // 15: content.spaces.FindResponse.spaces:type_name -> content.spaces.Space
+	4,  // 16: content.spaces.Spaces.Create:input_type -> content.spaces.CreateRequest
+	6,  // 17: content.spaces.Spaces.Get:input_type -> content.spaces.GetRequest
+	8,  // 18: content.spaces.Spaces.List:input_type -> content.spaces.ListRequest
+	19, // 19: content.spaces.Spaces.Find:input_type -> content.spaces.FindRequest
+	10, // 20: content.spaces.Spaces.Update:input_type -> content.spaces.UpdateRequest
+	11, // 21: content.spaces.Spaces.UpdateConfig:input_type -> content.spaces.UpdateConfigRequest
+	12, // 22: content.spaces.Spaces.Delete:input_type -> content.spaces.DeleteRequest
+	13, // 23: content.spaces.Spaces.Transfer:input_type -> content.spaces.TransferRequest
+	14, // 24: content.spaces.Spaces.AbortTransfer:input_type -> content.spaces.AbortTransferRequest
+	15, // 25: content.spaces.Spaces.ListTransfers:input_type -> content.spaces.ListTransfersRequest
+	17, // 26: content.spaces.Spaces.Move:input_type -> content.spaces.MoveRequest
+	5,  // 27: content.spaces.Spaces.Create:output_type -> content.spaces.CreateResponse
+	7,  // 28: content.spaces.Spaces.Get:output_type -> content.spaces.GetResponse
+	9,  // 29: content.spaces.Spaces.List:output_type -> content.spaces.ListResponse
+	20, // 30: content.spaces.Spaces.Find:output_type -> content.spaces.FindResponse
+	23, // 31: content.spaces.Spaces.Update:output_type -> google.protobuf.Empty
+	23, // 32: content.spaces.Spaces.UpdateConfig:output_type -> google.protobuf.Empty
+	23, // 33: content.spaces.Spaces.Delete:output_type -> google.protobuf.Empty
+	23, // 34: content.spaces.Spaces.Transfer:output_type -> google.protobuf.Empty
+	23, // 35: content.spaces.Spaces.AbortTransfer:output_type -> google.protobuf.Empty
+	16, // 36: content.spaces.Spaces.ListTransfers:output_type -> content.spaces.ListTransfersResponse
+	23, // 37: content.spaces.Spaces.Move:output_type -> google.protobuf.Empty
+	27, // [27:38] is the sub-list for method output_type
+	16, // [16:27] is the sub-list for method input_type
+	16, // [16:16] is the sub-list for extension type_name
+	16, // [16:16] is the sub-list for extension extendee
+	0,  // [0:16] is the sub-list for field type_name
 }
 
 func init() { file_spaces_spaces_proto_init() }
@@ -1226,7 +1456,7 @@ func file_spaces_spaces_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_spaces_spaces_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*Space); i {
 			case 0:
 				return &v.state
@@ -1238,7 +1468,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*StateInfo); i {
 			case 0:
 				return &v.state
@@ -1250,7 +1480,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*Config); i {
 			case 0:
 				return &v.state
@@ -1262,7 +1492,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -1274,7 +1504,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -1286,7 +1516,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -1298,7 +1528,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -1310,7 +1540,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*ListRequest); i {
 			case 0:
 				return &v.state
@@ -1322,7 +1552,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*ListResponse); i {
 			case 0:
 				return &v.state
@@ -1334,7 +1564,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -1346,7 +1576,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateConfigRequest); i {
 			case 0:
 				return &v.state
@@ -1358,7 +1588,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -1370,7 +1600,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[12].Exporter = func(v any, i int) any {
 			switch v := v.(*TransferRequest); i {
 			case 0:
 				return &v.state
@@ -1382,7 +1612,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[13].Exporter = func(v any, i int) any {
 			switch v := v.(*AbortTransferRequest); i {
 			case 0:
 				return &v.state
@@ -1394,7 +1624,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[14].Exporter = func(v any, i int) any {
 			switch v := v.(*ListTransfersRequest); i {
 			case 0:
 				return &v.state
@@ -1406,7 +1636,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[15].Exporter = func(v any, i int) any {
 			switch v := v.(*ListTransfersResponse); i {
 			case 0:
 				return &v.state
@@ -1418,7 +1648,7 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
-		file_spaces_spaces_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+		file_spaces_spaces_proto_msgTypes[16].Exporter = func(v any, i int) any {
 			switch v := v.(*MoveRequest); i {
 			case 0:
 				return &v.state
@@ -1430,6 +1660,42 @@ func file_spaces_spaces_proto_init() {
 				return nil
 			}
 		}
+		file_spaces_spaces_proto_msgTypes[17].Exporter = func(v any, i int) any {
+			switch v := v.(*Filter); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_spaces_spaces_proto_msgTypes[18].Exporter = func(v any, i int) any {
+			switch v := v.(*FindRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_spaces_spaces_proto_msgTypes[19].Exporter = func(v any, i int) any {
+			switch v := v.(*FindResponse); 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{
@@ -1437,7 +1703,7 @@ func file_spaces_spaces_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_spaces_spaces_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   17,
+			NumMessages:   20,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/proto/spaces/spaces_grpc.pb.go b/proto/spaces/spaces_grpc.pb.go
index f080fe31ba0c4d97ff3fa5fecbf608eaf2ccdd6d..a7d50aee15acb36577d0cf7ef56d29e3eceaaaaa 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.25.1
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: spaces/spaces.proto
 
 package spaces
@@ -16,13 +16,14 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Spaces_Create_FullMethodName        = "/content.spaces.Spaces/Create"
 	Spaces_Get_FullMethodName           = "/content.spaces.Spaces/Get"
 	Spaces_List_FullMethodName          = "/content.spaces.Spaces/List"
+	Spaces_Find_FullMethodName          = "/content.spaces.Spaces/Find"
 	Spaces_Update_FullMethodName        = "/content.spaces.Spaces/Update"
 	Spaces_UpdateConfig_FullMethodName  = "/content.spaces.Spaces/UpdateConfig"
 	Spaces_Delete_FullMethodName        = "/content.spaces.Spaces/Delete"
@@ -39,6 +40,7 @@ type SpacesClient interface {
 	Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error)
 	Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
 	List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
+	Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error)
 	Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
 	Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
@@ -70,8 +72,9 @@ func NewSpacesClient(cc grpc.ClientConnInterface) SpacesClient {
 }
 
 func (c *spacesClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Spaces_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -79,8 +82,9 @@ func (c *spacesClient) Create(ctx context.Context, in *CreateRequest, opts ...gr
 }
 
 func (c *spacesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Spaces_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -88,8 +92,19 @@ func (c *spacesClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Cal
 }
 
 func (c *spacesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListResponse)
-	err := c.cc.Invoke(ctx, Spaces_List_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_List_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *spacesClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(FindResponse)
+	err := c.cc.Invoke(ctx, Spaces_Find_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -97,8 +112,9 @@ func (c *spacesClient) List(ctx context.Context, in *ListRequest, opts ...grpc.C
 }
 
 func (c *spacesClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Spaces_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -106,8 +122,9 @@ func (c *spacesClient) Update(ctx context.Context, in *UpdateRequest, opts ...gr
 }
 
 func (c *spacesClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Spaces_UpdateConfig_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_UpdateConfig_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -115,8 +132,9 @@ func (c *spacesClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest
 }
 
 func (c *spacesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Spaces_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -124,8 +142,9 @@ func (c *spacesClient) Delete(ctx context.Context, in *DeleteRequest, opts ...gr
 }
 
 func (c *spacesClient) Transfer(ctx context.Context, in *TransferRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Spaces_Transfer_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_Transfer_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -133,8 +152,9 @@ func (c *spacesClient) Transfer(ctx context.Context, in *TransferRequest, opts .
 }
 
 func (c *spacesClient) AbortTransfer(ctx context.Context, in *AbortTransferRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Spaces_AbortTransfer_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_AbortTransfer_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -142,8 +162,9 @@ func (c *spacesClient) AbortTransfer(ctx context.Context, in *AbortTransferReque
 }
 
 func (c *spacesClient) ListTransfers(ctx context.Context, in *ListTransfersRequest, opts ...grpc.CallOption) (*ListTransfersResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(ListTransfersResponse)
-	err := c.cc.Invoke(ctx, Spaces_ListTransfers_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_ListTransfers_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -151,8 +172,9 @@ func (c *spacesClient) ListTransfers(ctx context.Context, in *ListTransfersReque
 }
 
 func (c *spacesClient) Move(ctx context.Context, in *MoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Spaces_Move_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Spaces_Move_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -161,11 +183,12 @@ func (c *spacesClient) Move(ctx context.Context, in *MoveRequest, opts ...grpc.C
 
 // SpacesServer is the server API for Spaces service.
 // All implementations must embed UnimplementedSpacesServer
-// for forward compatibility
+// for forward compatibility.
 type SpacesServer interface {
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
 	Get(context.Context, *GetRequest) (*GetResponse, error)
 	List(context.Context, *ListRequest) (*ListResponse, error)
+	Find(context.Context, *FindRequest) (*FindResponse, error)
 	Update(context.Context, *UpdateRequest) (*emptypb.Empty, error)
 	UpdateConfig(context.Context, *UpdateConfigRequest) (*emptypb.Empty, error)
 	Delete(context.Context, *DeleteRequest) (*emptypb.Empty, error)
@@ -189,9 +212,12 @@ type SpacesServer interface {
 	mustEmbedUnimplementedSpacesServer()
 }
 
-// UnimplementedSpacesServer must be embedded to have forward compatible implementations.
-type UnimplementedSpacesServer struct {
-}
+// UnimplementedSpacesServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedSpacesServer struct{}
 
 func (UnimplementedSpacesServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -202,6 +228,9 @@ func (UnimplementedSpacesServer) Get(context.Context, *GetRequest) (*GetResponse
 func (UnimplementedSpacesServer) List(context.Context, *ListRequest) (*ListResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method List not implemented")
 }
+func (UnimplementedSpacesServer) Find(context.Context, *FindRequest) (*FindResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Find not implemented")
+}
 func (UnimplementedSpacesServer) Update(context.Context, *UpdateRequest) (*emptypb.Empty, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Update not implemented")
 }
@@ -224,6 +253,7 @@ func (UnimplementedSpacesServer) Move(context.Context, *MoveRequest) (*emptypb.E
 	return nil, status.Errorf(codes.Unimplemented, "method Move not implemented")
 }
 func (UnimplementedSpacesServer) mustEmbedUnimplementedSpacesServer() {}
+func (UnimplementedSpacesServer) testEmbeddedByValue()                {}
 
 // UnsafeSpacesServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to SpacesServer will
@@ -233,6 +263,13 @@ type UnsafeSpacesServer interface {
 }
 
 func RegisterSpacesServer(s grpc.ServiceRegistrar, srv SpacesServer) {
+	// If the following call pancis, it indicates UnimplementedSpacesServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Spaces_ServiceDesc, srv)
 }
 
@@ -290,6 +327,24 @@ func _Spaces_List_Handler(srv interface{}, ctx context.Context, dec func(interfa
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Spaces_Find_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(FindRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(SpacesServer).Find(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: Spaces_Find_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(SpacesServer).Find(ctx, req.(*FindRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _Spaces_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(UpdateRequest)
 	if err := dec(in); err != nil {
@@ -435,6 +490,10 @@ var Spaces_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "List",
 			Handler:    _Spaces_List_Handler,
 		},
+		{
+			MethodName: "Find",
+			Handler:    _Spaces_Find_Handler,
+		},
 		{
 			MethodName: "Update",
 			Handler:    _Spaces_Update_Handler,
diff --git a/proto/users/users.pb.go b/proto/users/users.pb.go
index 18f49f1a3c2e75c911a5f783e9f008da8d9c4c79..23f9b300f1331883728dfb7a5355adba53e49306 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: users/users.proto
 
 package users
@@ -839,7 +839,7 @@ func file_users_users_proto_rawDescGZIP() []byte {
 }
 
 var file_users_users_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
-var file_users_users_proto_goTypes = []interface{}{
+var file_users_users_proto_goTypes = []any{
 	(*User)(nil),                  // 0: account.users.User
 	(*Filter)(nil),                // 1: account.users.Filter
 	(*CreateRequest)(nil),         // 2: account.users.CreateRequest
@@ -894,7 +894,7 @@ func file_users_users_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_users_users_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*User); i {
 			case 0:
 				return &v.state
@@ -906,7 +906,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[1].Exporter = func(v any, i int) any {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -918,7 +918,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[2].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateRequest); i {
 			case 0:
 				return &v.state
@@ -930,7 +930,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[3].Exporter = func(v any, i int) any {
 			switch v := v.(*CreateResponse); i {
 			case 0:
 				return &v.state
@@ -942,7 +942,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[4].Exporter = func(v any, i int) any {
 			switch v := v.(*GetRequest); i {
 			case 0:
 				return &v.state
@@ -954,7 +954,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[5].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
@@ -966,7 +966,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[6].Exporter = func(v any, i int) any {
 			switch v := v.(*FindRequest); i {
 			case 0:
 				return &v.state
@@ -978,7 +978,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[7].Exporter = func(v any, i int) any {
 			switch v := v.(*FindResponse); i {
 			case 0:
 				return &v.state
@@ -990,7 +990,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[8].Exporter = func(v any, i int) any {
 			switch v := v.(*UpdateRequest); i {
 			case 0:
 				return &v.state
@@ -1002,7 +1002,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[9].Exporter = func(v any, i int) any {
 			switch v := v.(*DeleteRequest); i {
 			case 0:
 				return &v.state
@@ -1014,7 +1014,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[10].Exporter = func(v any, i int) any {
 			switch v := v.(*GetByIdentityRequest); i {
 			case 0:
 				return &v.state
@@ -1026,7 +1026,7 @@ func file_users_users_proto_init() {
 				return nil
 			}
 		}
-		file_users_users_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+		file_users_users_proto_msgTypes[11].Exporter = func(v any, i int) any {
 			switch v := v.(*GetByIdentityResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/users/users_grpc.pb.go b/proto/users/users_grpc.pb.go
index 30a2b84431e53679e4b92f0f3db43ffc8d0e190c..20bc9b68454a328d389f4a45d77b4d2a8bb4cade 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: users/users.proto
 
 package users
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Users_Create_FullMethodName        = "/account.users.Users/Create"
@@ -59,8 +59,9 @@ func NewUsersClient(cc grpc.ClientConnInterface) UsersClient {
 }
 
 func (c *usersClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(CreateResponse)
-	err := c.cc.Invoke(ctx, Users_Create_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Users_Create_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -68,8 +69,9 @@ func (c *usersClient) Create(ctx context.Context, in *CreateRequest, opts ...grp
 }
 
 func (c *usersClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Users_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Users_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -77,8 +79,9 @@ func (c *usersClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.Call
 }
 
 func (c *usersClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.CallOption) (*FindResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(FindResponse)
-	err := c.cc.Invoke(ctx, Users_Find_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Users_Find_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -86,8 +89,9 @@ func (c *usersClient) Find(ctx context.Context, in *FindRequest, opts ...grpc.Ca
 }
 
 func (c *usersClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Users_Update_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Users_Update_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -95,8 +99,9 @@ func (c *usersClient) Update(ctx context.Context, in *UpdateRequest, opts ...grp
 }
 
 func (c *usersClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(emptypb.Empty)
-	err := c.cc.Invoke(ctx, Users_Delete_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Users_Delete_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -104,8 +109,9 @@ func (c *usersClient) Delete(ctx context.Context, in *DeleteRequest, opts ...grp
 }
 
 func (c *usersClient) GetByIdentity(ctx context.Context, in *GetByIdentityRequest, opts ...grpc.CallOption) (*GetByIdentityResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetByIdentityResponse)
-	err := c.cc.Invoke(ctx, Users_GetByIdentity_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Users_GetByIdentity_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -114,7 +120,7 @@ func (c *usersClient) GetByIdentity(ctx context.Context, in *GetByIdentityReques
 
 // UsersServer is the server API for Users service.
 // All implementations must embed UnimplementedUsersServer
-// for forward compatibility
+// for forward compatibility.
 type UsersServer interface {
 	// Создание пользователя или регистрация текущего пользователя в системе, если create.id == `current`
 	Create(context.Context, *CreateRequest) (*CreateResponse, error)
@@ -135,9 +141,12 @@ type UsersServer interface {
 	mustEmbedUnimplementedUsersServer()
 }
 
-// UnimplementedUsersServer must be embedded to have forward compatible implementations.
-type UnimplementedUsersServer struct {
-}
+// UnimplementedUsersServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedUsersServer struct{}
 
 func (UnimplementedUsersServer) Create(context.Context, *CreateRequest) (*CreateResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Create not implemented")
@@ -158,6 +167,7 @@ func (UnimplementedUsersServer) GetByIdentity(context.Context, *GetByIdentityReq
 	return nil, status.Errorf(codes.Unimplemented, "method GetByIdentity not implemented")
 }
 func (UnimplementedUsersServer) mustEmbedUnimplementedUsersServer() {}
+func (UnimplementedUsersServer) testEmbeddedByValue()               {}
 
 // UnsafeUsersServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to UsersServer will
@@ -167,6 +177,13 @@ type UnsafeUsersServer interface {
 }
 
 func RegisterUsersServer(s grpc.ServiceRegistrar, srv UsersServer) {
+	// If the following call pancis, it indicates UnimplementedUsersServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Users_ServiceDesc, srv)
 }
 
diff --git a/proto/versions/account/versions.pb.go b/proto/versions/account/versions.pb.go
index 9db5b3d2864ba0de22a950db158d66c3f488a36c..b1c95aba8a1f6a6d62ea2e0ac1102fe145f95178 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: versions/account/versions.proto
 
 package account
@@ -105,7 +105,7 @@ func file_versions_account_versions_proto_rawDescGZIP() []byte {
 }
 
 var file_versions_account_versions_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_versions_account_versions_proto_goTypes = []interface{}{
+var file_versions_account_versions_proto_goTypes = []any{
 	(*GetResponse)(nil),    // 0: account.GetResponse
 	(*common.Version)(nil), // 1: common.Version
 	(*emptypb.Empty)(nil),  // 2: google.protobuf.Empty
@@ -127,7 +127,7 @@ func file_versions_account_versions_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_versions_account_versions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_versions_account_versions_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/versions/account/versions_grpc.pb.go b/proto/versions/account/versions_grpc.pb.go
index 6d3fd60aff0ad0692c3a2ec698607ee4c59b9f97..06fb791413646cdc56dc021103c511d73689d7c8 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: versions/account/versions.proto
 
 package account
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Versions_Get_FullMethodName = "/account.Versions/Get"
@@ -39,8 +39,9 @@ func NewVersionsClient(cc grpc.ClientConnInterface) VersionsClient {
 }
 
 func (c *versionsClient) Get(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Versions_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Versions_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -49,20 +50,24 @@ func (c *versionsClient) Get(ctx context.Context, in *emptypb.Empty, opts ...grp
 
 // VersionsServer is the server API for Versions service.
 // All implementations must embed UnimplementedVersionsServer
-// for forward compatibility
+// for forward compatibility.
 type VersionsServer interface {
 	Get(context.Context, *emptypb.Empty) (*GetResponse, error)
 	mustEmbedUnimplementedVersionsServer()
 }
 
-// UnimplementedVersionsServer must be embedded to have forward compatible implementations.
-type UnimplementedVersionsServer struct {
-}
+// UnimplementedVersionsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedVersionsServer struct{}
 
 func (UnimplementedVersionsServer) Get(context.Context, *emptypb.Empty) (*GetResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
 }
 func (UnimplementedVersionsServer) mustEmbedUnimplementedVersionsServer() {}
+func (UnimplementedVersionsServer) testEmbeddedByValue()                  {}
 
 // UnsafeVersionsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to VersionsServer will
@@ -72,6 +77,13 @@ type UnsafeVersionsServer interface {
 }
 
 func RegisterVersionsServer(s grpc.ServiceRegistrar, srv VersionsServer) {
+	// If the following call pancis, it indicates UnimplementedVersionsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Versions_ServiceDesc, srv)
 }
 
diff --git a/proto/versions/content/versions.pb.go b/proto/versions/content/versions.pb.go
index c399b1a29d2e793d7918eaa456670d4b2a6d02ce..fd3d05b80b73f7f3195f93753457b20202d74b3b 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.31.0
-// 	protoc        v4.24.3
+// 	protoc-gen-go v1.34.2
+// 	protoc        v5.27.3
 // source: versions/content/versions.proto
 
 package content
@@ -105,7 +105,7 @@ func file_versions_content_versions_proto_rawDescGZIP() []byte {
 }
 
 var file_versions_content_versions_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_versions_content_versions_proto_goTypes = []interface{}{
+var file_versions_content_versions_proto_goTypes = []any{
 	(*GetResponse)(nil),    // 0: content.GetResponse
 	(*common.Version)(nil), // 1: common.Version
 	(*emptypb.Empty)(nil),  // 2: google.protobuf.Empty
@@ -127,7 +127,7 @@ func file_versions_content_versions_proto_init() {
 		return
 	}
 	if !protoimpl.UnsafeEnabled {
-		file_versions_content_versions_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+		file_versions_content_versions_proto_msgTypes[0].Exporter = func(v any, i int) any {
 			switch v := v.(*GetResponse); i {
 			case 0:
 				return &v.state
diff --git a/proto/versions/content/versions_grpc.pb.go b/proto/versions/content/versions_grpc.pb.go
index 25198d3383a04d58c15a85d83673445386e347b2..d2afb289fa36d77a4ec2e95abbe6c10ab7cd41f5 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             v4.24.3
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v5.27.3
 // source: versions/content/versions.proto
 
 package content
@@ -16,8 +16,8 @@ import (
 
 // 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
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
 
 const (
 	Versions_Get_FullMethodName = "/content.Versions/Get"
@@ -39,8 +39,9 @@ func NewVersionsClient(cc grpc.ClientConnInterface) VersionsClient {
 }
 
 func (c *versionsClient) Get(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetResponse, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
 	out := new(GetResponse)
-	err := c.cc.Invoke(ctx, Versions_Get_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, Versions_Get_FullMethodName, in, out, cOpts...)
 	if err != nil {
 		return nil, err
 	}
@@ -49,20 +50,24 @@ func (c *versionsClient) Get(ctx context.Context, in *emptypb.Empty, opts ...grp
 
 // VersionsServer is the server API for Versions service.
 // All implementations must embed UnimplementedVersionsServer
-// for forward compatibility
+// for forward compatibility.
 type VersionsServer interface {
 	Get(context.Context, *emptypb.Empty) (*GetResponse, error)
 	mustEmbedUnimplementedVersionsServer()
 }
 
-// UnimplementedVersionsServer must be embedded to have forward compatible implementations.
-type UnimplementedVersionsServer struct {
-}
+// UnimplementedVersionsServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedVersionsServer struct{}
 
 func (UnimplementedVersionsServer) Get(context.Context, *emptypb.Empty) (*GetResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
 }
 func (UnimplementedVersionsServer) mustEmbedUnimplementedVersionsServer() {}
+func (UnimplementedVersionsServer) testEmbeddedByValue()                  {}
 
 // UnsafeVersionsServer may be embedded to opt out of forward compatibility for this service.
 // Use of this interface is not recommended, as added methods to VersionsServer will
@@ -72,6 +77,13 @@ type UnsafeVersionsServer interface {
 }
 
 func RegisterVersionsServer(s grpc.ServiceRegistrar, srv VersionsServer) {
+	// If the following call pancis, it indicates UnimplementedVersionsServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
 	s.RegisterService(&Versions_ServiceDesc, srv)
 }
 
diff --git a/zap/field.go b/zap/field.go
index 4e5ec75e7f5fffbd6f1af83fd44c6a434d3190c3..d687bacc49c8e9599611e88590093c5756771896 100644
--- a/zap/field.go
+++ b/zap/field.go
@@ -2,7 +2,6 @@ package zap
 
 import (
 	"context"
-
 	"git.perx.ru/perxis/perxis-go/id"
 	_ "git.perx.ru/perxis/perxis-go/id/system" // регистрируем обработчики для системных объектов
 	"git.perx.ru/perxis/perxis-go/pkg/auth"
@@ -47,22 +46,42 @@ func Object(v any) zap.Field {
 	return zap.Reflect("object", oid)
 }
 
-// Caller возвращает поле и устанавливает передаваемый аргумент в качестве "вызывающего" в формате ObjectId.
-// Поддерживает типы в формате ObjectId: id.Descriptor, string, map[string]any, системные объекты.
-func Caller(v any) zap.Field {
-	oid, _ := id.NewObjectId(v)
-	return zap.Reflect("caller", oid)
+type callerConfig struct {
+	object  any
+	spaceID string
 }
 
-// CallerFromContext извлекает auth.Principal из контекста и устанавливает его в качестве "вызывающего" в формате Object.
-// Вторым параметром передается идентификатор пространства, который требуется, если вызывающий является auth.SpaceAccessor.
-// Если вызывающий не связан с пространством, следует передать пустую строку.
-func CallerFromContext(ctx context.Context, spaceID string) zap.Field {
-	principal := auth.GetPrincipal(ctx)
-	if accessor, ok := principal.(auth.SpaceAccessor); ok && spaceID != "" {
-		principal = accessor.Space(spaceID)
+type CallerOption func(c *callerConfig)
+
+func WithObject(object any) CallerOption {
+	return func(c *callerConfig) {
+		c.object = object
 	}
-	return Caller(principal)
+}
+
+func WithSpace(spaceID string) CallerOption {
+	return func(c *callerConfig) {
+		c.spaceID = spaceID
+	}
+}
+
+// Caller возвращает поле и устанавливает в зависимости от переданных опций "вызывающего" в формате ObjectId.
+func Caller(ctx context.Context, options ...CallerOption) zap.Field {
+	c := new(callerConfig)
+	for _, o := range options {
+		o(c)
+	}
+	var oid *id.ObjectId
+	if c.object != nil {
+		oid, _ = id.NewObjectId(c.object)
+	} else if ctx != nil {
+		principal := auth.GetPrincipal(ctx)
+		if accessor, ok := principal.(auth.SpaceAccessor); ok && c.spaceID != "" {
+			principal = accessor.Space(c.spaceID)
+		}
+		oid, _ = id.NewObjectId(principal)
+	}
+	return zap.Reflect("caller", oid)
 }
 
 func Attr(attr any) zap.Field {
diff --git a/zap/field_test.go b/zap/field_test.go
index 1e050bdcdc95a031cc00980309b650eac98c283b..46c4ee50722934e7d9ddd5ea759d01f3836706f8 100644
--- a/zap/field_test.go
+++ b/zap/field_test.go
@@ -132,43 +132,19 @@ func TestCaller(t *testing.T) {
 
 	oid := id.MustObjectId(user)
 	userId := id.NewUserId(user.ID)
-
-	tests := []struct {
-		name  string
-		field zap.Field
-		want  zap.Field
-	}{
-		{name: "system object", field: Caller(user), want: zap.Reflect("caller", oid)},
-		{name: "object id", field: Caller(userId), want: zap.Reflect("caller", oid)},
-		{name: "string", field: Caller(oid.String()), want: zap.Reflect("caller", oid)},
-		{name: "invalid", field: Caller(nil), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
-	}
-
-	for _, tc := range tests {
-		t.Run(tc.name, func(t *testing.T) {
-			wantObjectId, ok1 := tc.want.Interface.(*id.ObjectId)
-			fieldObjectId, ok2 := tc.field.Interface.(*id.ObjectId)
-
-			if ok1 && ok2 && wantObjectId != nil && fieldObjectId != nil {
-				assert.Equal(t, wantObjectId.String(), fieldObjectId.String())
-			} else {
-				assert.Equal(t, tc.want.Interface, tc.field.Interface)
-			}
-		})
-	}
-}
-
-func TestCallerFromContext(t *testing.T) {
 	ctx := auth.WithSystem(context.Background())
-	oid := id.MustObjectId(auth.GetPrincipal(ctx))
 
 	tests := []struct {
 		name  string
 		field zap.Field
 		want  zap.Field
 	}{
-		{name: "ok", field: CallerFromContext(ctx, ""), want: zap.Reflect("caller", oid)},
-		{name: "invalid", field: CallerFromContext(context.TODO(), ""), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
+		{name: "system object", field: Caller(context.TODO(), WithObject(user)), want: zap.Reflect("caller", oid)},
+		{name: "object id", field: Caller(context.TODO(), WithObject(userId)), want: zap.Reflect("caller", oid)},
+		{name: "string", field: Caller(context.TODO(), WithObject(oid.String())), want: zap.Reflect("caller", oid)},
+		{name: "invalid", field: Caller(context.TODO()), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
+		{name: "context", field: Caller(ctx), want: zap.Reflect("caller", id.MustObjectId(auth.GetPrincipal(ctx)))},
+		{name: "invalid context", field: Caller(context.TODO()), want: zap.Reflect("caller", (*id.ObjectId)(nil))},
 	}
 
 	for _, tc := range tests {