diff --git a/perxis-proto b/perxis-proto
index 81c967842f55811b459e455572703631712d7d86..8c2633f87320a29c7abd9389cedda60a64f88bfa 160000
--- a/perxis-proto
+++ b/perxis-proto
@@ -1 +1 @@
-Subproject commit 81c967842f55811b459e455572703631712d7d86
+Subproject commit 8c2633f87320a29c7abd9389cedda60a64f88bfa
diff --git a/pkg/extension/extension.go b/pkg/extension/extension.go
index fb8044c7a2f4c76b775840c5628319196a0493ae..4af0a7734b6bb2368c2cbe3ffd2f6de008e3340b 100644
--- a/pkg/extension/extension.go
+++ b/pkg/extension/extension.go
@@ -4,9 +4,12 @@ import (
 	"context"
 	"fmt"
 
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/pkg/items"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/setup"
 	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
 )
 
@@ -22,7 +25,7 @@ const (
 	StateInProgress = pb.SpaceExtensions_IN_PROGRESS
 	StateFail       = pb.SpaceExtensions_FAIL
 
-	ExtensionMetadataKey = "extension"
+	MetadataKey = "extension"
 )
 
 type (
@@ -105,3 +108,24 @@ func ExtensionFromError(err error) string {
 	ext, _ := v.(string)
 	return ext
 }
+
+func isMetadataEqual(s1, s2 *schema.Schema) bool {
+	if s1.Metadata == nil && s2.Metadata == nil {
+		return true
+	}
+	if s1.Metadata == nil || s2.Metadata == nil {
+		return false
+	}
+	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)
+	}
+	return setup.DefaultUpdateCollectionStrategyFn(s, exist, collection)
+}
diff --git a/pkg/extension/extension_test.go b/pkg/extension/extension_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c552b394f7a67cc9de5bb4aa25e90d22fe6058a
--- /dev/null
+++ b/pkg/extension/extension_test.go
@@ -0,0 +1,113 @@
+package extension
+
+import (
+	"testing"
+
+	"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"
+	"github.com/stretchr/testify/assert"
+)
+
+func Test_isMetadataEqual(t *testing.T) {
+	tests := []struct {
+		name string
+		s1   *schema.Schema
+		s2   *schema.Schema
+		want bool
+	}{
+		{
+			"Not equal #1 (no metadata)",
+			schema.New("name", field.String()).WithMetadata(MetadataKey, "test"),
+			schema.New("name", field.String()),
+			false,
+		},
+		{
+			"Not equal #2 (different metadata)",
+			schema.New("name", field.String()).WithMetadata(MetadataKey, "test"),
+			schema.New("name", field.String()).WithMetadata("test", "test"),
+			false,
+		},
+		{
+			"Equal #1 (no metadata)",
+			schema.New("name", field.String()),
+			schema.New("name", field.String()),
+			true,
+		},
+		{
+			"Equal #2 (equal metadata)",
+			schema.New("name", field.String()).WithMetadata(MetadataKey, "test"),
+			schema.New("name", field.String()).WithMetadata(MetadataKey, "test"),
+			true,
+		},
+	}
+	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)
+		})
+	}
+}
+
+func TestDefaultUpdateCollectionStrategyFn(t *testing.T) {
+	tests := []struct {
+		name       string
+		exist      *collections.Collection
+		collection *collections.Collection
+		force      bool
+		wantErr    func(err error)
+	}{
+		{
+			name: "collection belongs to 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,
+		},
+		{
+			name: "collection belongs to another 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")},
+			wantErr: func(err error) {
+				assert.ErrorIs(t, err, collections.ErrAlreadyExists)
+				assert.Equal(t, "Коллекция с идентификатором 'coll' уже существует. Удалите ее или "+
+					"вызовите установку расширения с флагом Force", errors.GetDetail(err))
+			},
+		},
+		{
+			name: "collection was created by user",
+			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))
+			},
+		},
+		{
+			name: "collection was created by user with 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-1")},
+			force:   true,
+			wantErr: nil,
+		},
+	}
+	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
+			}
+		})
+	}
+}
diff --git a/pkg/setup/collection.go b/pkg/setup/collection.go
index 494003d2f7cad1f8977a45913fe1b2ce8bb6c404..3b22ecf87118307d58ec942a5abef47aa439679b 100644
--- a/pkg/setup/collection.go
+++ b/pkg/setup/collection.go
@@ -8,8 +8,6 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/data"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
-	"git.perx.ru/perxis/perxis-go/pkg/extension"
-	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"go.uber.org/zap"
 )
 
@@ -73,42 +71,33 @@ func DeleteCollectionIfRemove() CollectionsOption {
 	}
 }
 
-func isMetadataExtensionEqual(s1, s2 *schema.Schema) bool {
-	if s1.Metadata == nil && s2.Metadata == nil {
-		return true
+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...))
 	}
 
-	if s1.Metadata == nil || s2.Metadata == nil {
-		return false
+	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)
+
+	if exist.View != nil && collection.View != nil {
+		update = update && *exist.View == *collection.View
 	}
 
-	return s1.Metadata[extension.ExtensionMetadataKey] == s2.Metadata[extension.ExtensionMetadataKey]
+	setSchema = !collection.IsView() && !reflect.DeepEqual(exist.Schema, collection.Schema)
+
+	return collection, update, setSchema, nil
 }
 
 func DefaultUpdateCollectionStrategy() CollectionsOption {
 	return func(c *CollectionConfig) {
-		c.UpdateFn = func(s *Setup, exist, collection *collections.Collection) (*collections.Collection, bool, bool, error) {
-			if !s.IsForce() && !collection.IsView() && !exist.IsView() && !isMetadataExtensionEqual(collection.Schema, exist.Schema) {
-				return nil, false, false, errors.WithDetailf(collections.ErrAlreadyExists, "Коллекция с идентификатором '%s' "+
-					"уже существует. Удалите ее или вызовите установку расширения с флагом Force", collection.ID)
-			}
-
-			if len(exist.Tags) > 0 {
-				collection.Tags = data.SetFromSlice(append(exist.Tags, collection.Tags...))
-			}
-
-			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)
-
-			if exist.View != nil && collection.View != nil {
-				update = update && *exist.View == *collection.View
-			}
-
-			setSchema = !collection.IsView() && !reflect.DeepEqual(exist.Schema, collection.Schema)
+		c.UpdateFn = DefaultUpdateCollectionStrategyFn
+	}
+}
 
-			return collection, update, setSchema, nil
-		}
+func WithUpdateCollectionStrategy(fn UpdateCollectionFn) CollectionsOption {
+	return func(c *CollectionConfig) {
+		c.UpdateFn = fn
 	}
 }
 
diff --git a/pkg/setup/collection_test.go b/pkg/setup/collection_test.go
index 78f414db18545994d8a15789c617c5f20c27674b..a06c117a3a5586b4a3afc9513bb6ac1333c556cf 100644
--- a/pkg/setup/collection_test.go
+++ b/pkg/setup/collection_test.go
@@ -10,7 +10,6 @@ import (
 	"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"
-	"git.perx.ru/perxis/perxis-go/pkg/extension"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
 	"github.com/stretchr/testify/assert"
@@ -107,64 +106,6 @@ func TestSetup_InstallCollections(t *testing.T) {
 				assert.Contains(t, errors.GetDetail(err), "Возникла ошибка при настройке коллекции space(1)")
 			},
 		},
-		{
-			name:        "Update extension collection with metadata",
-			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}},
-			collectionsCall: func(svc *mockscollections.Collections) {
-				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}, nil).Once()
-				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}).Return(nil).Once()
-				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")).Return(nil).Once()
-			},
-			envsCall: func(svc *envmocks.Environments) {
-				svc.On("Migrate", mock.Anything, "sp", "env").Return(nil).Once()
-			},
-			wantErr: func(t *testing.T, err error) {
-				assert.NoError(t, err)
-			},
-		},
-		{
-			name:        "Fail to update user collection with same id as in extensions collection",
-			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}},
-			collectionsCall: func(svc *mockscollections.Collections) {
-				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String())}, nil).Once()
-			},
-			wantErr: func(t *testing.T, err error) {
-				assert.Error(t, err)
-				assert.ErrorIs(t, err, collections.ErrAlreadyExists)
-			},
-		},
-		{
-			name:        "Update user collection with same id as in extensions collection with force",
-			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}},
-			collectionsCall: func(svc *mockscollections.Collections) {
-				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String())}, nil).Once()
-				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}).Return(nil).Once()
-				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")).Return(nil).Once()
-			},
-			envsCall: func(svc *envmocks.Environments) {
-				svc.On("Migrate", mock.Anything, "sp", "env").Return(nil).Once()
-			},
-			wantErr: func(t *testing.T, err error) {
-				assert.NoError(t, err)
-			},
-			force: true,
-		},
-		{
-			name:        "Update exist view collection with the same id",
-			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}},
-			collectionsCall: func(svc *mockscollections.Collections) {
-				svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: environments.DefaultEnvironment, CollectionID: "2"}}, nil).Once()
-				svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}).Return(nil).Once()
-				svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")).Return(nil).Once()
-			},
-			envsCall: func(svc *envmocks.Environments) {
-				svc.On("Migrate", mock.Anything, "sp", "env").Return(nil).Once()
-			},
-			wantErr: func(t *testing.T, err error) {
-				assert.NoError(t, err)
-			},
-		},
-		//todo после удаления флага _alwaysSetSchema данный тест будет работать, сейчас он будет всегда падать, поэтому пока закомментирован
 		{
 			name:        "Update view collection with the same id to new view collection",
 			collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}},
@@ -215,42 +156,3 @@ func TestSetup_InstallCollections(t *testing.T) {
 		})
 	}
 }
-
-func Test_isMetadataExtensionEqual(t *testing.T) {
-	tests := []struct {
-		name string
-		s1   *schema.Schema
-		s2   *schema.Schema
-		want bool
-	}{
-		{
-			"Not equal #1 (no metadata)",
-			schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"),
-			schema.New("name", field.String()),
-			false,
-		},
-		{
-			"Not equal #2 (different metadata)",
-			schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"),
-			schema.New("name", field.String()).WithMetadata("test", "test"),
-			false,
-		},
-		{
-			"Equal #1 (no metadata)",
-			schema.New("name", field.String()),
-			schema.New("name", field.String()),
-			true,
-		},
-		{
-			"Equal #2 (equal metadata)",
-			schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"),
-			schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"),
-			true,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			assert.Equalf(t, tt.want, isMetadataExtensionEqual(tt.s1, tt.s2), "isMetadataExtensionEqual(%v, %v)", tt.s1, tt.s2)
-		})
-	}
-}
diff --git a/pkg/setup/setup_test.go b/pkg/setup/setup_test.go
index 044d8294e6c7eb5cbdf28803c24064d39e3c7059..681158887cb53e81582615146b30e68cb2cdef67 100644
--- a/pkg/setup/setup_test.go
+++ b/pkg/setup/setup_test.go
@@ -12,7 +12,6 @@ import (
 	"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/extension"
 	"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"
@@ -66,7 +65,7 @@ func getActions() []*items.Item {
 			ID:           "act",
 			SpaceID:      spaceID,
 			EnvID:        envID,
-			CollectionID: extension.ActionsCollectionID,
+			CollectionID: "actions",
 			Data: map[string]interface{}{
 				"action":    "act",
 				"name":      "Action",
@@ -705,7 +704,7 @@ func TestSetupInstall(t *testing.T) {
 	//
 	//	itmMock := &itemsMock.Items{}
 	//	for _, act := range getActions() {
-	//		itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
+	//		itmMock.On("Get", mock.Anything, spaceID, envID, "actions", act.ID).
 	//			Return(nil, items.ErrNotFound).
 	//			Once()
 	//
@@ -775,7 +774,7 @@ func TestSetupUninstall(t *testing.T) {
 				del := args[1].(*items.Item)
 				require.Equal(t, spaceID, del.SpaceID)
 				require.Equal(t, envID, del.EnvID)
-				require.Equal(t, extension.ActionsCollectionID, del.CollectionID)
+				require.Equal(t, "actions", del.CollectionID)
 				require.Equal(t, act.ID, del.ID)
 			}).
 				Return(nil).
@@ -827,7 +826,7 @@ func TestSetupUninstall(t *testing.T) {
 				del := args[1].(*items.Item)
 				require.Equal(t, spaceID, del.SpaceID)
 				require.Equal(t, envID, del.EnvID)
-				require.Equal(t, extension.ActionsCollectionID, del.CollectionID)
+				require.Equal(t, "actions", del.CollectionID)
 				require.Equal(t, act.ID, del.ID)
 			}).
 				Return(nil).
@@ -863,7 +862,7 @@ func TestSetupUninstall(t *testing.T) {
 
 		itmMock := &itemsMock.Items{}
 		for _, act := range getActions() {
-			itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
+			itmMock.On("Delete", mock.Anything, spaceID, envID, "actions", act.ID).
 				Return(nil).
 				Once()
 		}
@@ -897,7 +896,7 @@ func TestSetupUninstall(t *testing.T) {
 
 		itmMock := &itemsMock.Items{}
 		for _, act := range getActions() {
-			itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
+			itmMock.On("Delete", mock.Anything, spaceID, envID, "actions", act.ID).
 				Return(nil).
 				Once()
 		}
@@ -937,7 +936,7 @@ func TestSetupUninstall(t *testing.T) {
 
 		itmMock := &itemsMock.Items{}
 		for _, act := range getActions() {
-			itmMock.On("Delete", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
+			itmMock.On("Delete", mock.Anything, spaceID, envID, "actions", act.ID).
 				Return(nil).
 				Once()
 		}
@@ -984,7 +983,7 @@ func TestSetupUninstall(t *testing.T) {
 				del := args[1].(*items.Item)
 				require.Equal(t, spaceID, del.SpaceID)
 				require.Equal(t, envID, del.EnvID)
-				require.Equal(t, extension.ActionsCollectionID, del.CollectionID)
+				require.Equal(t, "actions", del.CollectionID)
 				require.Equal(t, act.ID, del.ID)
 			}).
 				Return(errors.New("can't delete item")).
@@ -1047,7 +1046,7 @@ func TestSetupCheck(t *testing.T) {
 			mock.Anything,
 			spaceID,
 			envID,
-			extension.ActionsCollectionID,
+			"actions",
 			mock.MatchedBy(func(filter *items.Filter) bool { return data.Contains("act", filter.ID) }),
 			mock.MatchedBy(func(opt *items.FindOptions) bool { return opt.Regular && opt.Hidden && opt.Templates }),
 		).Return(getActions(), 0, nil).Once()
@@ -1219,7 +1218,7 @@ func TestSetupCheck(t *testing.T) {
 	//
 	//	itmMock := &itemsMock.Items{}
 	//	for _, act := range getActions() {
-	//		itmMock.On("Get", mock.Anything, spaceID, envID, extension.ActionsCollectionID, act.ID).
+	//		itmMock.On("Get", mock.Anything, spaceID, envID, "actions", act.ID).
 	//			Return(act, nil).
 	//			Once()
 	//	}