diff --git a/examples/extension_service/Dockerfile b/examples/extension_service/Dockerfile
index 8e11f941a29274ee56ef20e1ef74ced8f08d60c1..3335c7adf2b4752842ff6b515300a5ffe205d834 100644
--- a/examples/extension_service/Dockerfile
+++ b/examples/extension_service/Dockerfile
@@ -11,7 +11,7 @@ ARG PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}
 ENV PIP_EXTRA_INDEX_URL=$PIP_EXTRA_INDEX_URL
 
 COPY . /home/${USER}/app
-RUN pip install perxis==1.3.0
+RUN pip install perxis==1.10.0
 
 ENV PYTHONPATH="/home/perx/app"
 ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
diff --git a/examples/extension_service/Dockerfile.local b/examples/extension_service/Dockerfile.local
index 1b2d30650f663a69309125051451daf89915b2cc..9118f20159cc6c27e0a0d3f6ee69fc19dea50e78 100644
--- a/examples/extension_service/Dockerfile.local
+++ b/examples/extension_service/Dockerfile.local
@@ -11,7 +11,7 @@ ARG PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}
 ENV PIP_EXTRA_INDEX_URL=$PIP_EXTRA_INDEX_URL
 
 COPY . /home/${USER}/app
-RUN pip install perxis==1.3.0
+RUN pip install perxis==1.10.0
 RUN pip install 'watchdog[watchmedo]'
 
 ENV PYTHONPATH="/home/perx/app"
diff --git a/examples/extension_service/servicer.py b/examples/extension_service/servicer.py
index f83fd8657287cec29cd49de260676c9da1661f78..79aa7e5dee17ad97404b1f96034814b7c221b17c 100644
--- a/examples/extension_service/servicer.py
+++ b/examples/extension_service/servicer.py
@@ -4,6 +4,8 @@ from constants import extension
 from constants import collections as extension_collections
 
 
+from perxis.extensions.utils import datasource_items_from_collections, sync_policies_from_collections
+from perxis.extensions.item_models import DataSourceItem, SyncPolicyItem, Item, IfCollectionExists, IfExtensionInstalled
 from perxis.extensions.actions import make_action_dict
 from perxis.extensions import extension_service_pb2
 from perxis.extensions.extension_service import ExtensionService
@@ -76,6 +78,42 @@ class Servicer(ExtensionService):
         )
     ]
 
+    # items = (datasource_items_from_collections(extension_collections.schemes_mapping)
+    #         + sync_policies_from_collections(extension_collections.schemes_mapping))
+
+    items = [
+        DataSourceItem(
+            collection_id="test_collection"
+        ),
+        SyncPolicyItem(
+            collection_id="test_collection",
+            # Запрет на изменение или удаление записи политики синхронизации
+            with_update=False,
+            with_delete=False,
+        ),
+        Item(
+            collection_id="secrets",
+            data={
+                "id": f"{extension_id}__key",
+                "name": f"Ключ для {extension_id}",
+                "key": "123"
+            },
+            rules=[IfExtensionInstalled("secrets")]
+        ),
+        Item(
+            collection_id="web_domains",
+            identifier_field="domain",
+            data={
+                "domain": "example.com",
+                "primary": True,
+                "tls_ssl": "letsencrypt",
+            },
+            rules=[
+                IfCollectionExists()
+            ]
+        )
+    ]
+
     async def action_get_current_organization_and_users(
             self,
             request: extension_pb2.ActionRequest,
diff --git a/perxis/extensions/actions.py b/perxis/extensions/actions.py
index e2ae01878e76b8230da91abad8e564a534b60cce..a84976a736d20c585bc3882eb42b07d532b00d46 100644
--- a/perxis/extensions/actions.py
+++ b/perxis/extensions/actions.py
@@ -1,7 +1,5 @@
 import copy
 
-from typing import Optional
-
 from google.protobuf.struct_pb2 import Struct
 from perxis.items import items_pb2
 
diff --git a/perxis/extensions/bootstrap.py b/perxis/extensions/bootstrap.py
index c3e5c3ece363d0390bacea488906b6b0776285ba..4e3eb6634a70f09ecb24c6d30953f630838ed13e 100644
--- a/perxis/extensions/bootstrap.py
+++ b/perxis/extensions/bootstrap.py
@@ -100,6 +100,7 @@ async def _main(
             server = grpc.aio.server(futures.ThreadPoolExecutor(max_workers=10))
 
             servicer = servicer_cls(
+                ext_manager_service=ext_manager_stub,
                 collections_service=collections_stub,
                 environments_service=environments_stub,
                 roles_service=roles_stub,
diff --git a/perxis/extensions/extension_service.py b/perxis/extensions/extension_service.py
index 94a479ec9da68fca1f0767df629d8839ed788119..3799b340c03214128f30ce680362764c27c17cde 100644
--- a/perxis/extensions/extension_service.py
+++ b/perxis/extensions/extension_service.py
@@ -9,7 +9,7 @@ import dataclasses
 
 from google.protobuf import timestamp_pb2, any_pb2, wrappers_pb2
 
-from perxis.extensions import extension_service_pb2, extension_service_pb2_grpc, extension_pb2
+from perxis.extensions import extension_service_pb2, extension_service_pb2_grpc, extension_pb2, manager_service_pb2_grpc
 from perxis.collaborators import collaborators_pb2_grpc
 from perxis.invitations import invitations_pb2_grpc
 from perxis.locales import locales_pb2_grpc
@@ -27,6 +27,7 @@ from perxis.common import operation_pb2, operation_service_pb2_grpc, operation_s
 from perxis.collections import collections_pb2_grpc, collections_pb2
 from perxis.environments import environments_pb2_grpc
 from perxis.extensions.extension_setup import ExtensionSetup
+from perxis.extensions.item_models import AbstractItem
 
 
 def generate_operation_id() -> str:
@@ -93,10 +94,12 @@ class ExtensionService(
     roles: list[roles_pb2.Role] = []
     clients: list[clients_pb2.Client] = []
     actions: list[dict] = []
+    items: list[AbstractItem] = []
 
     __operations: dict[str, OperationMeta]
 
     def __init__(self,
+                 ext_manager_service: manager_service_pb2_grpc.ExtensionManagerServiceStub,
                  collections_service: collections_pb2_grpc.CollectionsStub,
                  environments_service: environments_pb2_grpc.EnvironmentsStub,
                  roles_service: roles_pb2_grpc.RolesStub,
@@ -115,6 +118,7 @@ class ExtensionService(
                  channel: grpc.Channel,
     ):
         self.logger = logging.getLogger(__name__)
+        self.ext_manager_service = ext_manager_service
         self.collections_service = collections_service
         self.environments_service = environments_service
         self.roles_service = roles_service
@@ -151,6 +155,20 @@ class ExtensionService(
         for action in self.actions or []:
             self.extension_setup.add_action(action)
 
+        services_list = [
+            (service_name, getattr(self, service_name))
+            for service_name
+            in self.__dict__
+            if service_name.endswith("_service")
+        ]
+        services_list.append(("channel", self.channel, ))
+
+        for item in self.items:
+            for rule in item.rules:
+                rule.bind_services(services_list)
+
+        self.extension_setup.set_items(self.items)
+
         @aiocron.crontab('0 * * * *', start=True)
         async def remove_old_operations():
             self.remove_old_operations()
@@ -251,10 +269,19 @@ class ExtensionService(
             request.space_id, request.env_id, request.force
         )
 
+        errors_list += await self.additional_install_operations(operation_id, request, context)
+
         self.result_log("установки", operation_id, request, errors_list)
 
         self.mark_operation_as_finished(operation_id, errors_list)
 
+    async def additional_install_operations(self, operation_id: str, request: extension_service_pb2.InstallRequest, context) -> list[str]:
+        """
+            Для доп. логики установки
+        """
+
+        return []
+
     async def Install(self, request: extension_service_pb2.InstallRequest, context):
         operation_id = generate_operation_id()
         operation_description = "Установка расширения %s для окружения %s пространства %s. %s force" % (
@@ -274,11 +301,19 @@ class ExtensionService(
 
     async def _Uninstall(self, operation_id: str, request: extension_service_pb2.UninstallRequest, context):
         errors_list: list[str] = await self.extension_setup.uninstall(request.space_id, request.env_id, request.remove)
+        errors_list += await self.additional_uninstall_operations(operation_id, request, context)
 
         self.result_log("удаления", operation_id, request, errors_list)
 
         self.mark_operation_as_finished(operation_id, errors_list)
 
+    async def additional_uninstall_operations(self, operation_id: str, request: extension_service_pb2.UninstallRequest, context) -> list[str]:
+        """
+            Для доп. логики удаления
+        """
+
+        return []
+
     async def Uninstall(self, request: extension_service_pb2.UninstallRequest, context):
         operation_id = generate_operation_id()
         operation_description = "Удаление расширения %s для окружения %s пространства %s. %s remove" % (
@@ -303,6 +338,13 @@ class ExtensionService(
 
         self.__operations[operation_id].mark_finished(errors_list)
 
+    async def additional_check_operations(self, operation_id: str, request: extension_service_pb2.CheckRequest, context) -> list[str]:
+        """"
+            Для доп. логики проверки
+        """
+
+        return []
+
     async def Check(self, request: extension_service_pb2.CheckRequest, context):
         operation_id = generate_operation_id()
         operation_description = "Проверка расширения %s для окружения %s пространства %s" % (
diff --git a/perxis/extensions/extension_setup.py b/perxis/extensions/extension_setup.py
index 4fa44311314ebaa67e594fc68eb4cef1d4a020cf..3bfa04ec060501c855bf05400cf38cc5f2c08489 100644
--- a/perxis/extensions/extension_setup.py
+++ b/perxis/extensions/extension_setup.py
@@ -16,6 +16,8 @@ from perxis.common import common_pb2
 from perxis.clients import clients_pb2_grpc, clients_pb2
 from perxis.environments import environments_pb2_grpc, environments_pb2
 from perxis.extensions.actions import make_action_item, ACTIONS_COLLECTION_ID
+from perxis.extensions.item_models import AbstractItem, SyncPolicyItem
+from perxis.provider import PerxisItemsWrapper
 
 
 logger = logging.getLogger(__name__)
@@ -34,6 +36,7 @@ class ExtensionSetup:
         self.clients = []
         self.roles = []
         self.actions = []
+        self.items = []
 
         self.roles_service = roles_service
         self.clients_service = clients_service
@@ -68,6 +71,9 @@ class ExtensionSetup:
     def set_clients(self, clients: list[clients_pb2.Client]):
         self.clients = clients
 
+    def set_items(self, items: list[AbstractItem]):
+        self.items = items
+
     # Работа с ролями
     async def __remove_roles(self, space_id: str) -> list[str]:
         errors_list: list[str] = []
@@ -571,40 +577,337 @@ class ExtensionSetup:
 
         return errors_list
 
+    async def __check_items(self, space_id: str, env_id: str) -> list[str]:
+        errors_list: list[str] = []
+        wrapper = PerxisItemsWrapper(
+            self.items_service,
+        )
+
+        for item in self.items:
+            all_rules_satisfied = await item.all_rules_is_satisfied(space_id, env_id)
+
+            if not all_rules_satisfied:
+                continue
+
+            try:
+                message = await wrapper.find(
+                    collection_id=item.collection_id,
+                    env_id=env_id,
+                    space_id=space_id,
+                    limit=1,
+                    offset=0,
+                    fields=["id"],
+                    filters=[f"{item.identifier_field} == '{item.identifier}'"]
+                )
+
+                if not message.total:
+                    errors_list.append(f"Item {item.identifier} не найден")
+            except Exception as e:
+                errors_list.append(
+                    f"Не удалось проверить item {item.identifier} "
+                    f"коллекции {item.collection_id} - {e}"
+                )
+
+        return errors_list
+
+    async def __update_items(self, space_id: str, env_id: str) -> list[str]:
+        errors_list: list[str] = []
+        wrapper = PerxisItemsWrapper(
+            self.items_service,
+        )
+
+        for item in self.items:
+            all_rules_satisfied = await item.all_rules_is_satisfied(space_id, env_id)
+
+            if not all_rules_satisfied:
+                continue
+
+            try:
+                message = await wrapper.find(
+                    collection_id=item.collection_id,
+                    env_id=env_id,
+                    space_id=space_id,
+                    limit=1,
+                    offset=0,
+                    filters=[f"{item.identifier_field} == '{item.identifier}'"]
+                )
+
+                if message.items:
+                    item_in_perxis = message.items[0]
+                else:
+                    item_in_perxis = None
+            except Exception as e:
+                if hasattr(e, "details") and "not found" in e.details():
+                    is_error = False
+                else:
+                    is_error = True
+
+                if is_error:
+                    errors_list.append(
+                        f"Не удалось получить item {item.identifier} "
+                        f"коллекции {item.collection_id} - {e}"
+                    )
+
+                continue
+
+            try:
+                if item_in_perxis:
+                    # Если установлен запрет на изменение item'ов
+                    if not item.with_update:
+                        continue
+
+                    # Для того чтобы не затереть изменения в perxis
+                    # Нужно смержить данные. Логика работы:
+                    # 1. Данные которые указаны в `data` в расширении - в приоритете, они замещают то что в perxis
+                    # 2. Данные которые есть в perxis но нет в расширении - дополняются
+                    await wrapper.update(
+                        collection_id=item.collection_id,
+                        item_id=item_in_perxis.id,
+                        space_id=space_id,
+                        env_id=env_id,
+                        data=item.merge_data(MessageToDict(item_in_perxis)["data"]),
+                    )
+                else:
+                    message = await wrapper.create(
+                        collection_id=item.collection_id,
+                        space_id=space_id,
+                        env_id=env_id,
+                        data=item.struct,
+                    )
+
+                    item_in_perxis = message.created
+
+                await wrapper.publish(
+                    item_id=item_in_perxis.id,
+                    collection_id=item.collection_id,
+                    space_id=space_id,
+                    env_id=env_id
+                )
+            except Exception as e:
+                errors_list.append(
+                    f"Не удалось записать item {item.identifier} "
+                    f"коллекции {item.collection_id} - {e}"
+                )
+
+                continue
+
+        return errors_list
+
+    async def __remove_items(self, space_id: str, env_id: str) -> list[str]:
+        errors_list: list[str] = []
+        wrapper = PerxisItemsWrapper(
+            self.items_service,
+        )
+
+        for item in self.items:
+            all_rules_satisfied = await item.all_rules_is_satisfied(space_id, env_id)
+
+            if not all_rules_satisfied:
+                continue
+
+            # Если установлен запрет на удаление item'ов
+            if not item.with_delete:
+                continue
+
+            try:
+                message = await wrapper.find(
+                    collection_id=item.collection_id,
+                    env_id=env_id,
+                    space_id=space_id,
+                    limit=1,
+                    offset=0,
+                    fields=["id"],
+                    filters=[f"{item.identifier_field} == '{item.identifier}'"]
+                )
+
+                if message.items:
+                    await wrapper.delete(
+                        item_id=message.items[0].id,
+                        collection_id=message.items[0].collection_id,
+                        space_id=space_id,
+                        env_id=env_id,
+                    )
+            except Exception as e:
+                if hasattr(e, "details") and "not found" in e.details():
+                    is_error = False
+                else:
+                    is_error = True
+
+                if is_error:
+                    errors_list.append(
+                        f"Не удалось удалить item {item.identifier} "
+                        f"коллекции {item.collection_id} - {e}"
+                    )
+
+        return errors_list
+
+    async def __update_view_role(self, space_id: str, env_id: str, mode: str = "add") -> list[str]:
+        errors = []
+
+        # Нужны только относящиеся к синхронизации элементы
+        items_for_view_role = [
+            item
+            for item
+            in self.items
+            if (isinstance(item, SyncPolicyItem) or item.collection_id == "hoop_item_sync_policies")
+            and item.data["export_view"]
+            and await item.all_rules_is_satisfied(space_id=space_id, env_id=env_id)
+        ]
+
+        if not items_for_view_role:
+            return errors
+
+        try:
+            message = await self.roles_service.Get(
+                roles_pb2.GetRequest(
+                    space_id=space_id,
+                    role_id="view"
+                )
+            )
+
+            role = message.role
+        except grpc.RpcError as e:
+            if "not found" not in e.details():
+                errors.append(f"Не удалось получить роль view, {e.details()}")
+
+            role = None
+
+        if not role:
+            try:
+                message = await self.roles_service.Create(
+                    roles_pb2.CreateRequest(
+                        role=roles_pb2.Role(
+                            id="view",
+                            space_id=space_id,
+                            description="Роль для view коллекций",
+                            rules=[],
+                            environments=["*"],
+                            allow_management=False,
+                        )
+                    ),
+                )
+
+                role = message.created
+            except grpc.RpcError as e:
+                errors.append(f"Не удалось создать роль view, {e.details()}")
+
+                return errors
+
+        # Произвести мерж правил доступа
+        for item in items_for_view_role:
+            actions_from_item = []
+
+            if not item.data["deny_read"]:
+                actions_from_item.append(common_pb2.READ)
+
+            if not item.data["deny_create"]:
+                actions_from_item.append(common_pb2.CREATE)
+
+            if not item.data["deny_publish"]:
+                actions_from_item.append(common_pb2.UPDATE)
+
+            if not item.data["deny_delete"]:
+                actions_from_item.append(common_pb2.DELETE)
+
+            rule_was_found = False
+            for rule in role.rules:
+                if rule.collection_id != item.data["collection"]:
+                    continue
+
+                rule_was_found = True
+
+                if mode == "add":
+                    modified_actions = list(set(list(rule.actions) + actions_from_item))
+                else:
+                    modified_actions = [
+                        action
+                        for action
+                        in rule.actions
+                        if action not in actions_from_item
+                    ]
+
+                rule.actions[:] = modified_actions
+
+            # Если правила для коллекции нет и при этом доступы нужно __добавлять__
+            if not rule_was_found and mode == "add":
+                role.rules.append(
+                    common_pb2.Rule(
+                        collection_id=item.data["collection"],
+                        actions=actions_from_item,
+                    )
+                )
+
+        try:
+            await self.roles_service.Update(
+                roles_pb2.UpdateRequest(
+                    role=role
+                )
+            )
+        except grpc.RpcError as e:
+            errors.append(f"Не удалось обновить роль view, {e.details()}")
+
+        return errors
+
+
     async def install(self, space_id: str, env_id: str, use_force: bool) -> list[str]:
         errors = []
 
-        errors += await self.__update_collections(space_id, env_id)
-        errors += await self.__update_roles(space_id, env_id)
-        errors += await self.__update_clients(space_id)
-        errors += await self.__update_actions(space_id, env_id)
+        try:
+            errors += await self.__update_collections(space_id, env_id)
+            errors += await self.__update_roles(space_id, env_id)
+            errors += await self.__update_clients(space_id)
+            errors += await self.__update_actions(space_id, env_id)
+            errors += await self.__update_items(space_id, env_id)
+            errors += await self.__update_view_role(space_id, env_id)
+        except Exception as e:
+            logger.exception(e)
+            errors.append(f"Во время установки было необработанное исключение - {e}")
 
         return errors
 
     async def update(self, space_id: str, env_id: str, use_force: bool) -> list[str]:
         errors = []
 
-        errors += await self.__update_collections(space_id, env_id)
-        errors += await self.__update_roles(space_id, env_id)
-        errors += await self.__update_clients(space_id)
-        errors += await self.__update_actions(space_id, env_id)
+        try:
+            errors += await self.__update_collections(space_id, env_id)
+            errors += await self.__update_roles(space_id, env_id)
+            errors += await self.__update_clients(space_id)
+            errors += await self.__update_actions(space_id, env_id)
+            errors += await self.__update_items(space_id, env_id)
+            errors += await self.__update_view_role(space_id, env_id)
+        except Exception as e:
+            logger.exception(e)
+            errors.append(f"Во время обновления было необработанное исключение - {e}")
 
         return errors
 
     async def check(self, space_id: str, env_id: str) -> list[str]:
-        errors = await self.__check_collections(space_id, env_id)
-        errors += await self.__check_roles(space_id)
-        errors += await self.__check_clients(space_id)
-        errors += await self.__check_actions(space_id, env_id)
+        errors = []
+
+        try:
+            errors += await self.__check_collections(space_id, env_id)
+            errors += await self.__check_roles(space_id)
+            errors += await self.__check_clients(space_id)
+            errors += await self.__check_actions(space_id, env_id)
+            errors += await self.__check_items(space_id, env_id)
+        except Exception as e:
+            logger.exception(e)
+            errors.append(f"Во время проверки было необработанное исключение - {e}")
 
         return errors
 
     async def uninstall(self, space_id: str, env_id: str, use_remove: bool) -> list[str]:
         errors = []
         if use_remove:
-            errors += await self.__remove_collections(space_id, env_id)
-            errors += await self.__remove_clients(space_id)
-            errors += await self.__remove_roles(space_id)
-            errors += await self.__remove_actions(space_id, env_id)
+            try:
+                errors += await self.__remove_collections(space_id, env_id)
+                errors += await self.__remove_clients(space_id)
+                errors += await self.__remove_roles(space_id)
+                errors += await self.__remove_actions(space_id, env_id)
+                errors += await self.__remove_items(space_id, env_id)
+                errors += await self.__update_view_role(space_id, env_id, "remove")
+            except Exception as e:
+                logger.exception(e)
+                errors.append(f"Во время удаления данных было необработанное исключение - {e}")
 
         return errors
diff --git a/perxis/extensions/item_models.py b/perxis/extensions/item_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..078ed22feb143938bbc0ed65dae5ae988af4bb97
--- /dev/null
+++ b/perxis/extensions/item_models.py
@@ -0,0 +1,148 @@
+import abc
+
+from google.protobuf.struct_pb2 import Struct
+
+from perxis.extensions.item_rules import AbstractRule, IfCollectionExists, IfExtensionInstalled
+
+
+class AbstractItem(metaclass=abc.ABCMeta):
+    """
+        Абстрактный класс для item'а. Нужен для определения общих свойств без реализации какого
+        то конкретного конструктора.
+    """
+
+    collection_id: str
+    data: dict
+
+    rules: list[AbstractRule]
+    identifier_field: str = "id"
+    with_update: bool
+    with_delete: bool
+
+    @property
+    def identifier(self):
+        return self.data[self.identifier_field]
+
+    @property
+    def struct(self) -> Struct:
+        s = Struct()
+        s.update(self.data)
+
+        return s
+
+    async def all_rules_is_satisfied(self, space_id: str, env_id: str) -> bool:
+        return all(
+            [
+                await rule(item=self, space_id=space_id, env_id=env_id)
+                for rule
+                in self.rules
+            ]
+        )
+
+    def merge_data(self, data_from_perxis: dict) -> Struct:
+        """
+            Мерж данных из перксиса и расширения. В приоритете данные расширения, если что-то
+            из них было вручную изменено в perxis - значение будет затёрто
+        """
+
+        s = Struct()
+        s.update({
+            **data_from_perxis,
+            **self.data,
+        })
+
+        return s
+
+
+class Item(AbstractItem):
+    """
+        Общий класс для любого Item
+    """
+
+    def __init__(
+        self,
+        collection_id: str,
+        data: dict,
+        with_update: bool = True,
+        with_delete: bool = True,
+        identifier_field: str = "id",
+        rules: list[AbstractRule] | None = None
+    ):
+        self.collection_id = collection_id
+        self.data = data
+        self.identifier_field = identifier_field
+        self.rules = rules or []
+        self.with_update = with_update
+        self.with_delete = with_delete
+
+
+class DataSourceItem(AbstractItem):
+    """
+        Класс для системной коллекции web_datasources
+    """
+
+    def __init__(
+        self,
+        collection_id: str,
+        rules: list[AbstractRule] | None = None,
+        query: str = "",
+        content_type: str = "",
+        exclude: bool = False,
+        with_update: bool = True,
+        with_delete: bool = True,
+    ):
+        self.collection_id = "web_datasources"
+        self.rules = rules or [IfCollectionExists()]
+        self.data = {
+            "id": collection_id,
+            "collection": collection_id,
+            "query": query,
+            "content_type": content_type,
+            "exclude": exclude,
+        }
+
+        self.with_update = with_update
+        self.with_delete = with_delete
+
+
+class SyncPolicyItem(AbstractItem):
+    """
+        Класс для коллекции hoop_item_sync_policies расширения perxishoop
+    """
+
+    identifier_field = "collection"
+
+    def __init__(
+        self,
+        collection_id: str,
+        name: str = "",
+        key: str = "id",
+        export_view: bool = True,
+        remove_collection: bool = False,
+        deny_publish: bool = True,
+        deny_delete: bool = True,
+        deny_create: bool = True,
+        deny_read: bool = False,
+        hidden: bool = False,
+        rules: list[AbstractRule] | None = None,
+        with_update: bool = True,
+        with_delete: bool = True,
+    ):
+        self.collection_id = "hoop_item_sync_policies"
+        self.rules = rules or [IfExtensionInstalled("perxishoop")]
+
+        self.data = {
+            "name": name or f"Коллекция {collection_id}",
+            "collection": collection_id,
+            "key": key,
+            "export_view": export_view,
+            "remove_collection": remove_collection,
+            "deny_publish": deny_publish,
+            "deny_delete": deny_delete,
+            "deny_create": deny_create,
+            "deny_read": deny_read,
+            "hidden": hidden,
+        }
+
+        self.with_update = with_update
+        self.with_delete = with_delete
\ No newline at end of file
diff --git a/perxis/extensions/item_rules.py b/perxis/extensions/item_rules.py
new file mode 100644
index 0000000000000000000000000000000000000000..106a98e28a942b7b4da215ec468227e30d7a8e01
--- /dev/null
+++ b/perxis/extensions/item_rules.py
@@ -0,0 +1,111 @@
+import abc
+import typing
+import logging
+
+from perxis.collections import collections_pb2
+from perxis.extensions import manager_service_pb2
+
+
+if typing.TYPE_CHECKING:
+    from perxis.extensions.item_models import AbstractItem
+
+
+class AbstractRule(metaclass=abc.ABCMeta):
+    """
+        Абстрактное правило для обработки создания item'ов в системе. Правила могут
+        использоваться в кейсах:
+            1. В случае если item опционален и невозможность его создания не приведёт к невозможности
+               использовать само расширение
+            2. Если при создании item'а нужно обогатить его данные чем-то внешним, не относящимся к
+               текущему расширению. Например - при создании item'ов для цепочки сервиса уведомлений.
+               Каждый последующий item может с помощью правил получать результат создания предыдущего
+               и на базе этих данных формировать ref
+
+        Так как сервисов в ExtensionService очень много, для того чтобы код их проброса в объект rule
+        не был излишне длинным - это делается неявным пробросом свойств из ExtensionService. Таким
+        образом и код короче получается, и в случае если будут добавлены какие то новые сервисы они
+        автоматически будут проброшены сюда.
+    """
+
+    @property
+    def rule_name(self):
+        return self.__class__.__name__
+
+    @property
+    def logger(self):
+        return logging.getLogger(self.rule_name)
+
+    def bind_services(self, service_list: list[tuple[str, object]]):
+        """
+            Нужно для автоматического подключения сервисов из ExtensionService. Проблема
+            в том что их очень много и если руками явно указывать - будет большой boilerplate
+        """
+
+        for service_name, service in service_list:
+            setattr(self, service_name, service)
+
+    @abc.abstractmethod
+    async def act(self, item: "AbstractItem", space_id: str, env_id: str):
+        raise NotImplementedError()
+
+    async def __call__(self, item: "AbstractItem", space_id: str, env_id: str) -> bool:
+        try:
+            await self.act(item, space_id, env_id)
+
+            return True
+        except Exception as e:
+            self.logger.warning(
+                f"Правило {self.rule_name} не "
+                f"сработало для {item.collection_id} (item {item.identifier}), "
+                f"ошибка - {e}"
+            )
+
+            return False
+
+
+class IfCollectionExists(AbstractRule):
+    """
+        Правило для опциональных item'ов которые нужно создавать в случае наличия
+        определённой коллекции в perxis. Может быть полезно если коллекция широко используется
+        но в данный момент устанавливается только вручную и не привязана ни к одному из расширений
+    """
+
+    async def act(self, item: "AbstractItem", space_id: str, env_id: str):
+        message = await self.collections_service.Get(
+            collections_pb2.GetRequest(
+                space_id=space_id,
+                env_id=env_id,
+                collection_id=item.collection_id
+            )
+        )
+
+        if not message.collection:
+            raise ValueError(f"Коллекция {item.collection_id} не найдена")
+
+
+class IfExtensionInstalled(AbstractRule):
+    """
+        Правило для опциональных item'ов которые нужно создавать только в случае
+        наличия определённого расширения в perxis
+    """
+
+    required_extension: str
+
+    def __init__(self, required_extension: str):
+        self.required_extension = required_extension
+
+    async def act(self, item: "AbstractItem", space_id: str, env_id: str):
+        registered_extensions = await self.ext_manager_service.ListRegisteredExtensions(
+            manager_service_pb2.ListRegisteredExtensionsRequest()
+        )
+
+        ext_is_installed = False
+        for ext in registered_extensions.extensions:
+            if ext.extension == self.required_extension:
+                ext_is_installed = True
+                break
+
+        if not ext_is_installed:
+            raise ValueError(
+                f"Расширение {self.required_extension} не установлено в perxis"
+            )
diff --git a/perxis/extensions/utils.py b/perxis/extensions/utils.py
index b5bbc4140df4b248d9dfc627732f45d7b7c7f7fb..d45242aebf6198710f77eeae98e4f40c88c6afca 100644
--- a/perxis/extensions/utils.py
+++ b/perxis/extensions/utils.py
@@ -1,6 +1,48 @@
 from typing import Optional
 
 from perxis.extensions import manager_service_pb2
+from perxis.extensions.item_models import DataSourceItem, SyncPolicyItem
+
+
+def datasource_items_from_collections(
+    collections_map: dict[str, str],
+    with_update: bool = True,
+    with_delete: bool = True,
+) -> list[DataSourceItem]:
+    """
+        Создание записей источников данных на базе маппинга коллекций
+    """
+
+    return [
+        DataSourceItem(
+            collection_id=collection_id,
+            with_delete=with_delete,
+            with_update=with_update
+        )
+        for collection_id in collections_map
+        if collection_id
+    ]
+
+
+def sync_policies_from_collections(
+    collections_map: dict[str, str],
+    with_update: bool = True,
+    with_delete: bool = True,
+) -> list[SyncPolicyItem]:
+    """
+        Создание записей синхронизации коллекций на базе маппинга коллекций
+    """
+
+    return [
+        SyncPolicyItem(
+            collection_id=collection_id,
+            name=collection_name,
+            with_update=with_update,
+            with_delete=with_delete,
+        )
+        for collection_id, collection_name in collections_map.items()
+        if collection_id
+    ]
 
 
 def get_extension_descriptor(
diff --git a/setup.py b/setup.py
index 78b8248f27d70760372801d583b30e69737143a4..2fb87abce6dcdc999169ff3de916c98a1157bc9c 100644
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@ def load_requirements():
 
 setup(
     name='perxis',
-    version='1.9.0',
+    version='1.10.0',
     description='Perxis python client',
     long_description=long_description,
     long_description_content_type='text/markdown',