diff --git a/pkg/collections/collection.go b/pkg/collections/collection.go
index 85ab01999816f8ab986c69495d737ce3f1e8160e..6583b4239912413dce66e4e4685fbe4868411e2a 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/optional"
 	"git.perx.ru/perxis/perxis-go/pkg/permission"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 )
@@ -199,3 +200,39 @@ func GetCollectionsIDs(collections []*Collection) []string {
 	}
 	return res
 }
+
+func FromSchemaMetadata(schemas ...*schema.Schema) []*Collection {
+	result := make([]*Collection, 0, len(schemas))
+	for _, sch := range schemas {
+		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 _, 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"],
+			}
+		}
+
+		result = append(result, coll)
+	}
+
+	return result
+}
diff --git a/pkg/collections/collection_test.go b/pkg/collections/collection_test.go
index 79e16a8e6a397418ef2418979bf0e38947b730c8..b98f6a14878b7e881f6a25ec51045e8b2101a6f0 100644
--- a/pkg/collections/collection_test.go
+++ b/pkg/collections/collection_test.go
@@ -3,6 +3,9 @@ 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"
 	"github.com/stretchr/testify/require"
 )
 
@@ -100,3 +103,84 @@ func TestView_Equal(t *testing.T) {
 		})
 	}
 }
+
+func TestFromSchemaMetadata(t *testing.T) {
+	testCases := []struct {
+		name    string
+		schemas []*schema.Schema
+		want    []*Collection
+	}{
+		{
+			name:    "Nil",
+			schemas: nil,
+			want:    []*Collection{},
+		},
+		{
+			name:    "Empty",
+			schemas: []*schema.Schema{},
+			want:    []*Collection{},
+		},
+		{
+			name:    "Without metadata",
+			schemas: []*schema.Schema{schema.New("a", field.String())},
+			want:    []*Collection{{Schema: schema.New("a", field.String())}},
+		},
+		{
+			name: "With metadata",
+			schemas: []*schema.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_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,
+				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_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: "Multiple",
+			schemas: []*schema.Schema{
+				schema.New("a", field.String()).WithMetadata("collection_id", "collID"),
+				schema.New("b", field.String()).WithMetadata("collection_name", "collName"),
+			},
+			want: []*Collection{
+				{
+					ID:     "collID",
+					Name:   "",
+					Schema: schema.New("a", field.String()).WithMetadata("collection_id", "collID"),
+				},
+				{
+
+					ID:     "",
+					Name:   "collName",
+					Schema: schema.New("b", field.String()).WithMetadata("collection_name", "collName"),
+				},
+			},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			result := FromSchemaMetadata(tt.schemas...)
+			require.Equal(t, tt.want, result)
+		})
+	}
+}
diff --git a/pkg/extension/extension.go b/pkg/extension/extension.go
index ae7878d56de53f423ea86c28fa2f263cef6cbd03..c5c1dd4cd32987fc0649d1fd8278e93fe794530a 100644
--- a/pkg/extension/extension.go
+++ b/pkg/extension/extension.go
@@ -2,6 +2,7 @@ package extension
 
 import (
 	"context"
+	"io/fs"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
@@ -95,3 +96,22 @@ func UpdateCollectionStrategy(s *setup.Setup, exist, collection *collections.Col
 	}
 	return setup.DefaultUpdateCollectionStrategyFn(s, exist, collection)
 }
+
+// CollectionsFromAssets инициализирует коллекции из файловой системы
+// В качестве файловой системы можно использовать файлы ОС `filesystem := os.DirFS("assets/schemas")`
+// Или файловую систему, встроенную в исполняемый файл при компиляции:
+//
+//	//go:embed assets/schemas/*
+//	var filesystem embed.FS
+func CollectionsFromAssets(filesystem fs.FS, extension string) []*collections.Collection {
+	schemas, err := schema.FromFS(filesystem)
+	if err != nil {
+		panic(err)
+	}
+
+	for _, schema := range schemas {
+		schema.WithMetadata(MetadataKey, extension)
+	}
+
+	return collections.FromSchemaMetadata(schemas...)
+}
diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go
index 1c3567e12756ac434de98a2dd7d25bb9b5f19ccb..4fec4c174684745be718abf4c2a595f7fe1ae3fe 100644
--- a/pkg/schema/schema.go
+++ b/pkg/schema/schema.go
@@ -2,7 +2,7 @@ package schema
 
 import (
 	"context"
-	"os"
+	"io/fs"
 	"path/filepath"
 	"reflect"
 
@@ -29,14 +29,13 @@ func NewFromField(f *field.Field) *Schema {
 
 // FromFile инициализирует и возвращает объект схемы из файла
 // Поддерживаются форматы JSON и YAML
-func FromFile(path string) (*Schema, error) {
-	file, err := os.Open(path)
+func FromFile(file fs.File) (*Schema, error) {
+	stat, err := file.Stat()
 	if err != nil {
 		return nil, err
 	}
-	defer file.Close()
 
-	switch filepath.Ext(path) {
+	switch filepath.Ext(stat.Name()) {
 	case ".json":
 		return FromJSON(file)
 	case ".yaml", ".yml":
@@ -46,19 +45,23 @@ func FromFile(path string) (*Schema, error) {
 	return nil, errors.New("schema must be in JSON or YAML format")
 }
 
-// FromFiles возвращает все валидные схемы в переданной директории
-func FromFiles(path string) ([]*Schema, error) {
-	files, err := os.ReadDir(path)
-	if err != nil {
-		return nil, err
-	}
+// FromFS возвращает все валидные схемы в переданной файловой системе
+func FromFS(fsys fs.FS) (result []*Schema, 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()
 
-	result := make([]*Schema, 0, len(files))
-	for _, file := range files {
-		if schema, err := FromFile(filepath.Join(path, file.Name())); err == nil {
+		if schema, err := FromFile(file); err == nil {
 			result = append(result, schema)
 		}
+		return nil
+	}); err != nil {
+		return nil, err
 	}
+
 	return result, nil
 }
 
diff --git a/pkg/schema/test/convert_test.go b/pkg/schema/test/convert_test.go
index 51ab33a6751440672c8a9a0dc0038f11898749b2..1f6149ce375f4f4863483c8392e849a85db6e0f8 100644
--- a/pkg/schema/test/convert_test.go
+++ b/pkg/schema/test/convert_test.go
@@ -1,6 +1,7 @@
 package test
 
 import (
+	"os"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/extension"
@@ -23,10 +24,6 @@ func TestFromFile(t *testing.T) {
 			path:    "assets/not_schema.txt",
 			wantErr: "schema must be in JSON or YAML format",
 		},
-		{
-			path:    "non/existent.json",
-			wantErr: "open non/existent.json: no such file or directory",
-		},
 		{
 			path:    "assets/invalid.json",
 			wantErr: "error unmarshal json into field",
@@ -41,7 +38,10 @@ func TestFromFile(t *testing.T) {
 		},
 	} {
 		t.Run(tt.path, func(t *testing.T) {
-			result, err := schema.FromFile(tt.path)
+			file, err := os.Open(tt.path)
+			require.NoError(t, err)
+
+			result, err := schema.FromFile(file)
 			if tt.wantErr != "" {
 				require.Error(t, err)
 				assert.ErrorContains(t, err, tt.wantErr)
@@ -56,14 +56,14 @@ func TestFromFile(t *testing.T) {
 
 func TestFromFiles(t *testing.T) {
 	t.Run("Non-existen path", func(t *testing.T) {
-		schemas, err := schema.FromFiles("non-existen")
+		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.FromFiles("assets")
+		schemas, err := schema.FromFS(os.DirFS("assets"))
 		require.NoError(t, err)
 		require.Len(t, schemas, 2, "В директории хранятся две корректные схемы")
 		require.Equal(t, schemas[0], schemas[1], "Они одинаковые, но в разных форматах")