Compare commits

...

64 Commits

Author SHA1 Message Date
Dmitry 1f52e74754 Разбить заметку по инфраструктурной безопасности 2026-06-16 21:54:17 +03:00
Dmitry 080eda2482 ada-pc: 2026-06-16 21:28:49 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 21:28:49 +03:00
Dmitry 70309d2b21 ada-pc: 2026-06-16 20:58:39 | 5
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
99 System/Cache/Pasted image 20260616205628.png
99 System/Cache/Pasted image 20260616205632.png
99 System/Cache/Pasted image 20260616205634.png
99 System/Cache/Pasted image 20260616205715.png
2026-06-16 20:58:39 +03:00
Dmitry 6028b36f14 ada-pc: 2026-06-16 20:53:38 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 20:53:38 +03:00
Dmitry 46f5b022fe ada-pc: 2026-06-16 20:48:37 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 20:48:37 +03:00
Dmitry 2eccd21884 ada-pc: 2026-06-16 20:43:36 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 20:43:36 +03:00
Dmitry 71b02a5ec8 ada-pc: 2026-06-16 20:38:35 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 20:38:35 +03:00
Dmitry 78c4c851e9 ada-pc: 2026-06-16 20:28:33 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 20:28:33 +03:00
Dmitry 1ad8968f56 ada-pc: 2026-06-16 20:23:32 | 1
Affected files:
00 Inbox/Безопасность инфраструктуры. ZTNA, SASE, DiD.md
2026-06-16 20:23:32 +03:00
Dmitry 22eda53d79 ada-pc: 2026-06-16 20:18:31 | 9
Affected files:
.obsidian/graph.json
99 System/INDEX.md
99 System/Tools/agent-tools/AGENT_TOOLS.md
99 System/Tools/agent-tools/vault_audit.py
99 System/Tools/agent-tools/vault_common.py
99 System/Tools/agent-tools/vault_frontmatter.py
99 System/Tools/agent-tools/vault_index.py
99 System/Tools/agent-tools/vault_ingest.py
AGENTS.md
2026-06-16 20:18:32 +03:00
Dmitry 3b597aad7d ada-pc: 2026-06-16 20:13:30 | 8
Affected files:
99 System/Export/agent-tools/AGENT_TOOLS.md
99 System/Export/agent-tools/vault_audit.py
99 System/Export/agent-tools/vault_common.py
99 System/Export/agent-tools/vault_frontmatter.py
99 System/Export/agent-tools/vault_index.py
99 System/Export/agent-tools/vault_ingest.py
99 System/INDEX.md
AGENTS.md
2026-06-16 20:13:30 +03:00
Dmitry be68b1a239 ada-pc: 2026-06-16 20:08:29 | 1
Affected files:
99 System/INDEX.md
2026-06-16 20:08:29 +03:00
Dmitry 1b3ccc6f35 ada-pc: 2026-06-16 20:03:28 | 2
Affected files:
90 Library/DevOps/CI-CD — основы.md
99 System/Archive/CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов.md
2026-06-16 20:03:28 +03:00
Dmitry 8d741e418f ada-pc: 2026-06-16 17:56:57 | 1
Affected files:
00 Inbox/CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов.md
2026-06-16 17:56:57 +03:00
Dmitry f1acef4dab ada-pc: 2026-06-16 17:51:56 | 1
Affected files:
00 Inbox/CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов.md
2026-06-16 17:51:57 +03:00
Dmitry de388b14ea ada-pc: 2026-06-16 17:46:56 | 5
Affected files:
.obsidian/app.json
.obsidian/appearance.json
.obsidian/core-plugins.json
.obsidian/plugins/obsidian-style-settings/data.json
00 Inbox/Без названия.md
2026-06-16 17:46:56 +03:00
Dmitry 5db8346292 ada-pc: 2026-06-16 17:41:49 | 1
Affected files:
.obsidian/plugins/editing-toolbar/data.json
2026-06-16 17:41:49 +03:00
Dmitry 3a86aa4d6a ada-pc: 2026-06-15 22:02:34 | 1
Affected files:
.obsidian/appearance.json
2026-06-15 22:02:34 +03:00
Dmitry a38ed7a5a7 ada-pc: 2026-06-15 21:57:22 | 9
Affected files:
01 Ideas/.gitkeep
02 Projects/.gitkeep
03 Journal/.gitkeep
90 Library/.gitkeep
99 System/.gitkeep
99 System/Archive/.gitkeep
99 System/Cache/.gitkeep
99 System/Export/.gitkeep
99 System/Template/.gitkeep
2026-06-15 21:57:22 +03:00
Dmitry cd69326e89 ada-pc: 2026-06-15 19:46:04 | 2
Affected files:
.gitignore
.obsidian/plugins/obsidian-icon-folder/data.json
2026-06-15 19:46:05 +03:00
Dmitry c2b9ea9dcc Merge remote-tracking branch 'origin/main' 2026-06-15 19:44:19 +03:00
Dmitry 95ac50594e Merge remote-tracking branch 'origin/main' 2026-06-15 19:44:05 +03:00
Dmitry c25c6fd615 ada-pc: 2026-06-15 19:44:02 | 1
Affected files:
.obsidian/plugins/obsidian-icon-folder/data.json
2026-06-15 19:44:02 +03:00
Dmitry 4282b925f7 arch-x1: 2026-06-15 19:43:57 | 2
Affected files:
.trash/Без названия 3.md
00 Inbox/.gitkeep
2026-06-15 19:43:57 +03:00
Dmitry 155497d932 arch-x1: 2026-06-15 19:42:54 | 1
Affected files:
99 System/INDEX.md
2026-06-15 19:42:54 +03:00
Dmitry b28230288c arch-x1: 2026-06-15 19:38:47 | 6
Affected files:
90 Library/HomeLab/Proxmox VE — local, local-lvm, LVM.md
Inbox/Proxmox VE — local, local-lvm, LVM.md
90 Library/Linux/Sudoers.md
Inbox/Sudoers.md
99 System/Archive/Proxmox VE — local, local-lvm, LVM — source.md
99 System/Archive/Sudoers — source.md
2026-06-15 19:38:47 +03:00
Dmitry ad3d377e33 Merge remote-tracking branch 'origin/main' 2026-06-15 19:06:26 +03:00
Dmitry b28ffe377c ada-pc: 2026-06-15 19:06:23 | 1
Affected files:
.obsidian/appearance.json
2026-06-15 19:06:23 +03:00
Dmitry cb9a9874ab arch-x1: 2026-06-15 14:51:57 | 4
Affected files:
.obsidian/appearance.json
.obsidian/plugins/editing-toolbar/data.json
00 Inbox/Sudoers.md
99 System/INDEX.md
2026-06-15 14:51:57 +03:00
Dmitry f5e9ea6fd7 arch-x1: 2026-06-15 14:36:56 | 1
Affected files:
00 Inbox/Proxmox VE — local, local-lvm, LVM.md
2026-06-15 14:36:56 +03:00
Dmitry e93882817d arch-x1: 2026-06-15 14:31:56 | 1
Affected files:
.obsidian/plugins/obsidian-icon-folder/data.json
2026-06-15 14:31:56 +03:00
Dmitry 2db9d82c59 arch-x1: 2026-06-15 11:48:23 | 1
Affected files:
99 System/Export/Экономический анализ - экзамен/Экономический анализ - шпора к экзамену.pdf
2026-06-15 11:48:23 +03:00
Dmitry 95227e0f42 arch-x1: 2026-06-15 11:38:23 | 1
Affected files:
99 System/Export/Экономический анализ - экзамен/Экономический анализ - шпора к экзамену.pdf
2026-06-15 11:38:23 +03:00
Dmitry 1f2b4711ab arch-x1: 2026-06-15 11:33:23 | 2
Affected files:
99 System/Export/Экономический анализ - экзамен/Экономический анализ - шпора к экзамену.html
99 System/Export/Экономический анализ - экзамен/Экономический анализ - шпора к экзамену.pdf
2026-06-15 11:33:23 +03:00
Dmitry a572bad302 arch-x1: 2026-06-15 11:28:22 | 1
Affected files:
99 System/Export/Экономический анализ - экзамен/Экономический анализ - шпора к экзамену.html
2026-06-15 11:28:22 +03:00
Dmitry a88369d800 arch-x1: 2026-06-15 11:23:22 | 2
Affected files:
.obsidian/plugins/obsidian-icon-folder/data.json
99 System/Export/Экономический анализ - экзамен/Экономический анализ - шпора к экзамену.html
2026-06-15 11:23:22 +03:00
Dmitry 03a58cda27 ada-pc: 2026-06-15 08:36:19 | 3
Affected files:
.obsidian/plugins/obsidian-linter/data.json
90 Library/HomeLab/Infrastructure — ada-dev.md
99 System/Cache/infra.excalidraw.md.md
2026-06-15 08:36:19 +03:00
Dmitry 0130b2980a ada-pc: 2026-06-15 08:31:17 | 1
Affected files:
99 System/Cache/infra.excalidraw.md.md
2026-06-15 08:31:18 +03:00
Dmitry 3a507fa69b ada-pc: 2026-06-15 08:26:17 | 1
Affected files:
99 System/Cache/infra.excalidraw.md.md
2026-06-15 08:26:17 +03:00
Dmitry bafed635c2 ada-pc: 2026-06-15 08:21:16 | 2
Affected files:
.obsidian/plugins/obsidian-excalidraw-plugin/data.json
99 System/Cache/infra.excalidraw.md.md
2026-06-15 08:21:16 +03:00
Dmitry f67061a06a ada-pc: 2026-06-14 20:51:13 | 1
Affected files:
02 Projects/Наука/НИР/УИР_Сводный_вариант.md
2026-06-14 20:51:14 +03:00
Dmitry e3adae23df ada-pc: 2026-06-12 12:54:15 | 3
Affected files:
90 Library/DevOps/CI-CD — основы.md
99 System/Cache/Pasted image 20260612125256.png
99 System/Cache/Pasted image 20260612125409.png
2026-06-12 12:54:15 +03:00
Dmitry 4c394b3f6b ada-pc: 2026-06-12 11:25:50 | 3
Affected files:
90 Library/DevOps/CI-CD — основы.md
99 System/Archive/CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins.md
99 System/INDEX.md
2026-06-12 11:25:50 +03:00
Dmitry 101cc6141f ada-pc: 2026-06-10 23:02:44 | 1
Affected files:
00 Inbox/CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins.md
2026-06-10 23:02:44 +03:00
Dmitry 4182fab2cd ada-pc: 2026-06-10 22:52:42 | 1
Affected files:
00 Inbox/CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins.md
2026-06-10 22:52:42 +03:00
Dmitry e87145d6b9 ada-pc: 2026-06-10 22:47:42 | 5
Affected files:
.trash/Pasted image 20260610224414.png
.trash/Pasted image 20260610224417.png
.trash/Pasted image 20260610224420.png
.trash/Pasted image 20260610224422.png
00 Inbox/CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins.md
2026-06-10 22:47:42 +03:00
Dmitry c405d5a5ed ada-pc: 2026-06-10 22:32:38 | 6
Affected files:
.trash/Система контроля версий. Знакомство с Git.md
.trash/УИР_Учёт_посещаемости_МИФИ.md
00 Inbox/CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins.md
02 Projects/Наука/НИР/УИР_Сводный_вариант.md
99 System/Archive/gemini-code-1780921978295.md
99 System/Archive/Обзор аналогов и обоснование инструментария системы учёта посещаемости студентов НИЯУ МИФИ.md
2026-06-10 22:32:39 +03:00
Dmitry 793c2ebf3f ada-pc: 2026-06-10 22:27:38 | 1
Affected files:
00 Inbox/Система контроля версий. Знакомство с Git.md
2026-06-10 22:27:38 +03:00
Dmitry 7247dca120 ada-pc: 2026-06-10 22:22:37 | 1
Affected files:
00 Inbox/Система контроля версий. Знакомство с Git.md
2026-06-10 22:22:37 +03:00
Dmitry 62d50b94b1 ada-pc: 2026-06-10 22:17:36 | 2
Affected files:
.obsidian/plugins/editing-toolbar/data.json
00 Inbox/Система контроля версий. Знакомство с Git.md
2026-06-10 22:17:36 +03:00
Dmitry 8091cea606 ada-pc: 2026-06-09 20:34:19 | 1
Affected files:
.obsidian/plugins/editing-toolbar/data.json
2026-06-09 20:34:19 +03:00
Dmitry 50b2b29133 arch-x1: 2026-06-09 15:18:14 | 1
Affected files:
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 15:18:14 +03:00
Dmitry 87d31a739e arch-x1: 2026-06-09 15:13:14 | 2
Affected files:
.obsidian/plugins/obsidian-linter/data.json
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 15:13:14 +03:00
Dmitry dc2c900470 arch-x1: 2026-06-09 15:08:13 | 2
Affected files:
.obsidian/community-plugins.json
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 15:08:13 +03:00
Dmitry 2dbeda37ee arch-x1: 2026-06-09 14:33:12 | 1
Affected files:
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 14:33:12 +03:00
Dmitry 108cc447b4 arch-x1: 2026-06-09 14:28:12 | 1
Affected files:
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 14:28:12 +03:00
Dmitry 8a3f69c387 arch-x1: 2026-06-09 14:23:12 | 1
Affected files:
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 14:23:12 +03:00
Dmitry 783a6535f7 arch-x1: 2026-06-09 14:18:11 | 1
Affected files:
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 14:18:11 +03:00
Dmitry 9976af5090 arch-x1: 2026-06-09 10:59:16 | 1
Affected files:
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 10:59:16 +03:00
Dmitry f43476441a arch-x1: 2026-06-09 10:54:16 | 3
Affected files:
.obsidian/plugins/editing-toolbar/data.json
.obsidian/plugins/obsidian-style-settings/data.json
00 Inbox/УИР_Сводный_вариант.md
2026-06-09 10:54:16 +03:00
Dmitry 165a4d6a49 arch-x1: 2026-06-09 10:49:10 | 2
Affected files:
.gitignore
.obsidian/plugins/obsidian-style-settings/data.json
2026-06-09 10:49:10 +03:00
Dmitry f3b2ac7136 ada-pc: 2026-06-09 05:57:55 | 5
Affected files:
.obsidian/plugins/editing-toolbar/data.json
.trash/Без названия 1.md
.trash/Без названия 2.md
00 Inbox/gemini-code-1780921978295.md
00 Inbox/Без названия.md
2026-06-09 05:57:55 +03:00
Dmitry 37bc0017c6 ada-pc: 2026-06-09 05:52:54 | 1
Affected files:
00 Inbox/Без названия.md
2026-06-09 05:52:54 +03:00
Dmitry 5799cc5faa ada-pc: 2026-06-08 23:32:27 | 1
Affected files:
.gitignore
2026-06-08 23:32:27 +03:00
63 changed files with 66925 additions and 264 deletions
+1
View File
@@ -5,6 +5,7 @@ Thumbs.db
*.swp *.swp
*.swo *.swo
.obsidian .obsidian
.obsidian/
.claude .claude
!".obsidian\\plugins\\obsidian-git" !".obsidian\\plugins\\obsidian-git"
+3 -2
View File
@@ -5,7 +5,7 @@
"attachmentFolderPath": "99 System/Cache", "attachmentFolderPath": "99 System/Cache",
"trashOption": "local", "trashOption": "local",
"showUnsupportedFiles": true, "showUnsupportedFiles": true,
"promptDelete": false, "promptDelete": true,
"pdfExportSettings": { "pdfExportSettings": {
"includeName": false, "includeName": false,
"pageSize": "Legal", "pageSize": "Legal",
@@ -22,5 +22,6 @@
"userIgnoreFilters": [ "userIgnoreFilters": [
"99 System/" "99 System/"
], ],
"showInlineTitle": true "showInlineTitle": true,
"uriCallbacks": true
} }
+3 -3
View File
@@ -5,10 +5,10 @@
"math-blocks" "math-blocks"
], ],
"theme": "obsidian", "theme": "obsidian",
"interfaceFontFamily": "Fira Sans,FiraCode Nerd Font", "interfaceFontFamily": "Fira Sans",
"textFontFamily": "Fira Sans,FiraCode Nerd Font", "textFontFamily": "Fira Sans",
"baseFontSizeAction": true, "baseFontSizeAction": true,
"baseFontSize": 16, "baseFontSize": 15,
"monospaceFontFamily": "FiraCode Nerd Font", "monospaceFontFamily": "FiraCode Nerd Font",
"nativeMenus": false, "nativeMenus": false,
"showRibbon": true, "showRibbon": true,
+2 -1
View File
@@ -11,5 +11,6 @@
"obsidian-git", "obsidian-git",
"obsidian-excalidraw-plugin", "obsidian-excalidraw-plugin",
"terminal", "terminal",
"typewriter-mode" "typewriter-mode",
"typographer"
] ]
+3 -3
View File
@@ -3,9 +3,9 @@
"global-search": true, "global-search": true,
"switcher": true, "switcher": true,
"graph": true, "graph": true,
"backlink": true, "backlink": false,
"canvas": true, "canvas": true,
"outgoing-link": true, "outgoing-link": false,
"tag-pane": true, "tag-pane": true,
"footnotes": true, "footnotes": true,
"properties": true, "properties": true,
@@ -19,7 +19,7 @@
"bookmarks": true, "bookmarks": true,
"markdown-importer": true, "markdown-importer": true,
"zk-prefixer": false, "zk-prefixer": false,
"random-note": true, "random-note": false,
"outline": true, "outline": true,
"word-count": true, "word-count": true,
"slides": false, "slides": false,
+1 -1
View File
@@ -39,6 +39,6 @@
"repelStrength": 6.34095634095634, "repelStrength": 6.34095634095634,
"linkStrength": 0.504158004158004, "linkStrength": 0.504158004158004,
"linkDistance": 250, "linkDistance": 250,
"scale": 0.2882782914407446, "scale": 0.21176529377630143,
"close": true "close": true
} }
+29 -80
View File
@@ -1,13 +1,8 @@
{ {
"lastVersion": "4.0.8", "lastVersion": "4.0.8",
"aestheticStyle": "glass", "aestheticStyle": "custom",
"positionStyle": "top", "positionStyle": "fixed",
"menuCommands": [ "menuCommands": [
{
"id": "editing-toolbar:ai-tools",
"name": "AI Tools",
"icon": "lucide-sparkles"
},
{ {
"id": "editing-toolbar:editor-undo", "id": "editing-toolbar:editor-undo",
"name": "Undo editor", "name": "Undo editor",
@@ -394,19 +389,8 @@
"icon": "exit-fullscreen" "icon": "exit-fullscreen"
} }
], ],
"followingCommands": [ "followingCommands": [],
{
"id": "editing-toolbar:ai-tools",
"name": "AI Tools",
"icon": "lucide-sparkles"
}
],
"topCommands": [ "topCommands": [
{
"id": "editing-toolbar:ai-tools",
"name": "AI Tools",
"icon": "lucide-sparkles"
},
{ {
"id": "editing-toolbar:editor-undo", "id": "editing-toolbar:editor-undo",
"name": "Undo editor", "name": "Undo editor",
@@ -427,43 +411,6 @@
"name": "Clear text formatting", "name": "Clear text formatting",
"icon": "eraser" "icon": "eraser"
}, },
{
"id": "editing-toolbar:header2-text",
"name": "Header 2",
"icon": "header-2"
},
{
"id": "editing-toolbar:header3-text",
"name": "Header 3",
"icon": "header-3"
},
{
"id": "SubmenuCommands-header",
"name": "submenu",
"icon": "header-n",
"SubmenuCommands": [
{
"id": "editing-toolbar:header1-text",
"name": "Header 1",
"icon": "header-1"
},
{
"id": "editing-toolbar:header4-text",
"name": "Header 4",
"icon": "header-4"
},
{
"id": "editing-toolbar:header5-text",
"name": "Header 5",
"icon": "header-5"
},
{
"id": "editing-toolbar:header6-text",
"name": "Header 6",
"icon": "header-6"
}
]
},
{ {
"id": "editing-toolbar:toggle-bold", "id": "editing-toolbar:toggle-bold",
"name": "Bold", "name": "Bold",
@@ -794,11 +741,6 @@
} }
], ],
"fixedCommands": [ "fixedCommands": [
{
"id": "editing-toolbar:ai-tools",
"name": "AI Tools",
"icon": "lucide-sparkles"
},
{ {
"id": "editing-toolbar:editor-undo", "id": "editing-toolbar:editor-undo",
"name": "Undo editor", "name": "Undo editor",
@@ -1087,13 +1029,7 @@
"icon": "exit-fullscreen" "icon": "exit-fullscreen"
} }
], ],
"mobileCommands": [ "mobileCommands": [],
{
"id": "editing-toolbar:ai-tools",
"name": "AI Tools",
"icon": "lucide-sparkles"
}
],
"enableMultipleConfig": true, "enableMultipleConfig": true,
"enableTopToolbar": true, "enableTopToolbar": true,
"enableFollowingToolbar": false, "enableFollowingToolbar": false,
@@ -1102,7 +1038,7 @@
"shouldShowMenuOnSelect": false, "shouldShowMenuOnSelect": false,
"cMenuVisibility": true, "cMenuVisibility": true,
"cMenuBottomValue": 4.25, "cMenuBottomValue": 4.25,
"cMenuNumRows": 12, "cMenuNumRows": 1,
"cMenuWidth": 610, "cMenuWidth": 610,
"cMenuFontColor": "#2DC26B", "cMenuFontColor": "#2DC26B",
"cMenuBackgroundColor": "#d3f8b6", "cMenuBackgroundColor": "#d3f8b6",
@@ -1135,13 +1071,13 @@
"toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)", "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
"toolbarIconColor": "var(--text-normal)", "toolbarIconColor": "var(--text-normal)",
"toolbarIconSize": 18, "toolbarIconSize": 18,
"aestheticStyle": "default" "aestheticStyle": "glass"
}, },
"fixed": { "fixed": {
"toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)", "toolbarBackgroundColor": "#F5F8FA",
"toolbarIconColor": "var(--text-normal)", "toolbarIconColor": "#4A5568",
"toolbarIconSize": 18, "toolbarIconSize": 12,
"aestheticStyle": "default" "aestheticStyle": "custom"
}, },
"mobile": { "mobile": {
"toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)", "toolbarBackgroundColor": "rgba(var(--background-secondary-rgb), 0.7)",
@@ -1150,12 +1086,12 @@
"aestheticStyle": "default" "aestheticStyle": "default"
} }
}, },
"toolbarBackgroundColor": "#2D3033", "toolbarBackgroundColor": "#F5F8FA",
"toolbarIconColor": "#E2E8F0", "toolbarIconColor": "#4A5568",
"toolbarIconSize": 18, "toolbarIconSize": 12,
"useCurrentLineForRegex": false, "useCurrentLineForRegex": false,
"ai": { "ai": {
"enabled": true, "enabled": false,
"consentAccepted": true, "consentAccepted": true,
"onboardingShown": true, "onboardingShown": true,
"providerMode": "pkmer-first", "providerMode": "pkmer-first",
@@ -1176,8 +1112,21 @@
"artifact": "03-agent" "artifact": "03-agent"
}, },
"pkmer": { "pkmer": {
"tokenExpiresAt": 0, "tokenExpiresAt": 1781624499398,
"userInfo": null "userInfo": {
"sub": "fa484d57-e07b-48f3-9410-ff0ce39782d8",
"name": "gcdmitry",
"email": "gcdmitry@gmail.com",
"ai_quota": {
"quota": 49875000,
"usedQuota": 125000,
"remainingQuota": 49875000,
"remainingTokens": 99,
"requestCount": 1
},
"thino": false,
"supporter": false
}
}, },
"enableCustomModel": false, "enableCustomModel": false,
"customModel": { "customModel": {
+1 -1
View File
@@ -120,7 +120,7 @@
"library2": { "library2": {
"type": "excalidrawlib", "type": "excalidrawlib",
"version": 2, "version": 2,
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/2.23.0", "source": "https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag2.23.12",
"libraryItems": [] "libraryItems": []
}, },
"imageElementNotice": true, "imageElementNotice": true,
+12 -12
View File
@@ -6,11 +6,11 @@
"emojiStyle": "native", "emojiStyle": "native",
"iconColor": null, "iconColor": null,
"recentlyUsedIcons": [ "recentlyUsedIcons": [
"🧰", "LiToolCase",
"🎒", "LiLibrary",
"LiComputer", "LiCalendar",
"💡", "LiNotebook",
"🏔" "LiLightbulb"
], ],
"recentlyUsedIconsSize": 5, "recentlyUsedIconsSize": 5,
"rules": [], "rules": [],
@@ -34,17 +34,16 @@
"debugMode": false, "debugMode": false,
"useInternalPlugins": false "useInternalPlugins": false
}, },
"00 Inbox": "📥", "99 System": "LiToolCase",
"99 System": "🧰",
"99 System/Template": "LiBookTemplate", "99 System/Template": "LiBookTemplate",
"99 System/Archive": "LiFolderArchive", "99 System/Archive": "LiFolderArchive",
"99 System/Cache": "LiArchive", "99 System/Cache": "LiArchive",
"02 Projects": "📔", "02 Projects": "LiNotebook",
"03 Journal": "📅", "03 Journal": "LiCalendar",
"02 Projects/Наука": "LiShieldQuestion", "02 Projects/Наука": "LiShieldQuestion",
"02 Projects/Учеба": "LiNotebookTabs", "02 Projects/Учеба": "LiNotebookTabs",
"01 Ideas": "💡", "01 Ideas": "LiLightbulb",
"90 Library": "", "90 Library": "LiLibrary",
"90 Library/02 DevOps": "LiSpline", "90 Library/02 DevOps": "LiSpline",
"90 Library/04 Networking": "LiNetwork", "90 Library/04 Networking": "LiNetwork",
"90 Library/05 Operating System": "LiComputer", "90 Library/05 Operating System": "LiComputer",
@@ -68,5 +67,6 @@
"01 Library/09 Sport": "LiDumbbell", "01 Library/09 Sport": "LiDumbbell",
"01 Library/10 Finance": "LiCircleDollarSign", "01 Library/10 Finance": "LiCircleDollarSign",
"01 Library/07 HomeLab": "LiServer", "01 Library/07 HomeLab": "LiServer",
"01 Library/01 Admin": "🏔" "01 Library/01 Admin": "🏔",
"00 Inbox": "LiInbox"
} }
+8 -2
View File
@@ -149,7 +149,7 @@
"enabled": true, "enabled": true,
"single-quote-enabled": true, "single-quote-enabled": true,
"single-quote-style": "''", "single-quote-style": "''",
"double-quote-enabled": true, "double-quote-enabled": false,
"double-quote-style": "\"\"" "double-quote-style": "\"\""
}, },
"remove-consecutive-list-markers": { "remove-consecutive-list-markers": {
@@ -282,7 +282,13 @@
"foldersToIgnore": [ "foldersToIgnore": [
"99 System" "99 System"
], ],
"filesToIgnore": [], "filesToIgnore": [
{
"label": "Excalidraw",
"match": ".*\\.excalidraw(\\.md)?$",
"flags": "i"
}
],
"linterLocale": "system-default", "linterLocale": "system-default",
"logLevel": "ERROR", "logLevel": "ERROR",
"lintCommands": [], "lintCommands": [],
+5 -6
View File
@@ -32,8 +32,8 @@
"shiba-theme-settings@@translucent-pane-style-settings": "shib-setting-default-frosted-glass", "shiba-theme-settings@@translucent-pane-style-settings": "shib-setting-default-frosted-glass",
"shiba-theme-settings@@shib-transparent-setting-panel": false, "shiba-theme-settings@@shib-transparent-setting-panel": false,
"Plugin@@colorful-checkbox": true, "Plugin@@colorful-checkbox": true,
"anuppuccin-theme-settings@@anuppuccin-theme-dark": "ctp-macchiato", "anuppuccin-theme-settings@@anuppuccin-theme-dark": "ctp-mocha",
"anuppuccin-theme-settings@@anuppuccin-theme-accents": "ctp-accent-sapphire", "anuppuccin-theme-settings@@anuppuccin-theme-accents": "ctp-accent-lavender",
"anuppuccin-theme-settings@@anp-active-line": "anp-current-line-border-only", "anuppuccin-theme-settings@@anp-active-line": "anp-current-line-border-only",
"anuppuccin-theme-settings@@anp-pdf-blend-toggle-dark": false, "anuppuccin-theme-settings@@anp-pdf-blend-toggle-dark": false,
"anuppuccin-theme-settings@@anp-alt-tab-style": "anp-alternate-tab-toggle", "anuppuccin-theme-settings@@anp-alt-tab-style": "anp-alternate-tab-toggle",
@@ -64,9 +64,7 @@
"pln@@pln-attachments-pdf": true, "pln@@pln-attachments-pdf": true,
"pln@@pln-attachments-images": true, "pln@@pln-attachments-images": true,
"pln@@pln-hdcl": true, "pln@@pln-hdcl": true,
"anuppuccin-theme-settings@@anp-toggle-preview": false, "anuppuccin-theme-settings@@anp-toggle-preview": true,
"anuppuccin-theme-settings@@file-margins": 60,
"anuppuccin-theme-settings@@anp-preview-width-pct": 75,
"anuppuccin-theme-settings@@anp-layout-select": "anp-card-layout", "anuppuccin-theme-settings@@anp-layout-select": "anp-card-layout",
"anuppuccin-theme-settings@@anp-toggle-metadata": true, "anuppuccin-theme-settings@@anp-toggle-metadata": true,
"anuppuccin-theme-settings@@anp-toggle-scrollbars": true, "anuppuccin-theme-settings@@anp-toggle-scrollbars": true,
@@ -109,5 +107,6 @@
"anuppuccin-theme-settings@@anp-translucency-opacity": 0.5, "anuppuccin-theme-settings@@anp-translucency-opacity": 0.5,
"anuppuccin-theme-settings@@anp-editor-font-source": "Inter", "anuppuccin-theme-settings@@anp-editor-font-source": "Inter",
"anuppuccin-theme-settings@@anp-editor-font-lp": "Inter", "anuppuccin-theme-settings@@anp-editor-font-lp": "Inter",
"anuppuccin-theme-settings@@anp-editor-font-rv": "Inter" "anuppuccin-theme-settings@@anp-editor-font-rv": "Inter",
"anuppuccin-theme-settings@@file-line-width": 900
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

+8
View File
@@ -0,0 +1,8 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-09
aliases: []
---
+8
View File
@@ -0,0 +1,8 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-09
aliases: []
---
+8
View File
@@ -0,0 +1,8 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-02-27
aliases: []
---
@@ -0,0 +1,223 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-10
aliases: []
---
# Система контроля версий. Знакомство с Git
## Git
Неоспоримыми преимуществами Git являются:
- Высокая производительность.
- Безопасность.
- Гибкость в распределенных системах.
- Прекрасные возможности для командной работы.
### Производительность
Высокая производительность Git обусловлена подходом по оптимизации внутренних процедур и использованию анализа содержимого файлов Git работает с файлами, храня объекты с содержимым каталога и метаданными их версий.
### Безопасность
Безопасность при работе с Git обеспечивается целостностью исходного кода и применением алгоритма шифрования SHA1.
Использование Git гарантирует подлинность истории изменений и защищает исходный код от тайного внесения изменений.
### Гибкость
Гибкость при работе с Git достигается за счет поддержки линейных и нелинейных циклов разработки, совместимости со множеством других информационных систем и популярных протоколов.
### Возможности для командной работы
Git обладает впечатляющими возможностями для командной работы за счет поддержки множества разнообразных моделей управления исходным кодом, удовлетворяющих нужды больших и маленьких команд, простых и сложных распределенных проектов
## Часть 3. Модели управления исходным кодом
Работа с версиями исходного кода в Git построена на основе использования:
- Коммитов (Commits).
- Веток (Branches).
- Слияния веток (Merge).
- Сравнения версий (Diff).
Большой популярностью до сих пор пользуется модель под названием Gitflow.
### Gitflow
**Gitflow** — модель ветвления в Git, в которой используются основные ветки (Main, Кelease, Вevelop) и функциональные ветки (Feature).
В качестве основных веток в Gitflow часто используются:
- Main (Ex. master).
- Release.
- Develop (Dev).
В ветке Main хранится официальная история релизов, в Release ветках концентрируется функционал готово к выпуску релиза продукта, а ветка Develop предназначена для разработки функционала.
В качестве функциональных веток в `Gitflow` используются *Feature* ветки, ответвленные от основной ветки *Develop*.
Работа над каждым новым функционалом ведется командой в собственной *Feature-ветке*.
После завершения разработки функционала, каждая *Feature-ветка* **сливается** с *Develop-веткой*.
Когда в *Develop-ветке* оказывается достаточно функционала для выпуска нового релиза, на основе Develop-ветки создается ветка Release.
После прохождения всех тестов и проверок, Release-ветка сливается с остальными основными ветками — Main и Develop.
**Весь цикл разработки в модели Gitflow можно визуализировать** **следующим образом:**
![](https://legacy.merionet.ru/images/devops/images/3756/img_01.png)
**Модель Gitflow обладает и недостатками:**
- Не лучшая совместимость с современными процессами CI/CD.
- Не подходит для рабочих процессов, основывающихся на подходах, отличных от регулярного выпуска релизов.
- Потенциально запутанная схема веток и трудности в восстановлении историчности их слияний в сложных проектах.
На данный момент, Gitflow является недостаточно универсальной моделью рабочего процесса разработки и считается устаревшей.
Предпочтительной для современных процессов разработки является модель магистральных рабочих процессов (TBD).
### TBD
**TBD (Trunk Based Development)** — альтернативная модель управления исходным кодом в Git на основе ветвления, пришедшая на смену устаревшей модели Gitflow.
Модель TBD основана на принципе использования одной главной ветки, называемой “магистралью” (Trunk).
Вся работа над новым функционалом ведется разработчиками именно в магистральной ветке, что исключает трудности, связанные со слиянием и неработающими сборками.
Команда разработки, ведущая работу над новым функционалом, сохраняет свои изменения только в Trunk-ветку и обеспечивает непрерывную сборку, тестирование и доставку нового функционала, не привязываясь к срокам и частоте выпуска релизов.
При использовании модели TBD и регулярном добавлении в основную Trunk-ветку функционала, а также настроенных стабильных процессов сборки и доставки продукта (CI/CD), выпустить релиз можно практически в любой момент.
**Модель TBD можно визуализировать следующим образом:**
![](https://legacy.merionet.ru/images/devops/images/3756/img_02.png)
Модель TDB быстро завоевала популярность в разветвленных проектах и прекрасно зарекомендовала себя в распределенных больших командах разработки за счет своей понятности, динамичности и совместимости с CI/CD-процессами.
## Часть 4. Знакомство с Gitlab
### Gitlab
**Gitlab** — это веб-приложение, обеспечивающее управление репозиториями программного кода в системе контроля версий Git.
Установить и использовать Gitlab можно на собственном сервере или в облачной инфраструктуре.
Gitlab позволяет:
- Создавать, изменять и удалять репозитории.
- Управлять пользователями и их правами.
- Автоматизировать процессы CI/CD и тестирования.
- Полнофункционально взаимодействовать с исходным кодом из веб-интерфейса.
Основные преимущества Gitlab:
- Гибкие возможности по планированию командной разработки.
- Создание и управление проектами.
- Построение процессов тестирования кода.
- Непрерывная сборка и доставка (CI/CD).
- Наблюдаемость (Observability).
Gitlab позволяет эффективно поддерживать и развивать выбранный вами подход по управлению исходным кодом и командной разработкой (Gitflow, TDB и другие).
Работа с репозиториями в Gitlab осуществляется посредством создания, изменения, управления, удаления проектов и работы с пользователями.
Функциональность Gitlab позволяет задействовать инструменты по управлению ветками и коммитами пользователей, обеспечивая слаженную командную работу.
В Gitlab реализованы инструменты для проведения следующих операций над исходным кодом:
- Code-review.
- Оценка качества кода.
- Тестирование.
Возможна настройка модели приемки качества исходного кода, его проверки и тестирования.
Gitlab обладает интегрированными инструментами, позволяющими автоматизировать рутинные операции с исходным кодом:
- Непрерывную сборку и доставку (CI/CD).
- Тестирование новых версий.
- Релизный цикл продукта.
- Проверки по безопасности.
Gitlab позволяет отслеживать и обрабатывать множество важной информации о процессе разработки:
- Трекинг затраченного рабочего времени на разработку функционала и выпуск новых релизов.
- Мониторинг работоспособности приложения.
- Расширенный сбор и анализ метрик.
## Часть 5. Основные команды git
### Команды Git
#### `git init`
Создание нового Git-репозитория (преобразование существующего проекта или создание нового пустого репозитория).
#### `git clone`
Создание локальной копии удаленного Git-репозитория.
#### `git branch`
Создание отдельной ветки на основе существующей для осуществления в новой ветке процесса разработки с возможным последующим слиянием.
#### `git checkout`
Переключение между различными версиями целевого объекта (файлы, коммиты и ветки).
#### `git status`
Отображение рабочего состояния файлов в локальной копии отслеживаемого проекта.
#### `git add`
Индексирование изменений в локальной копии отслеживаемого проекта.
#### `git commit`
Подготовка к отправке в удаленный репозиторий набора проиндексированных изменений в локальном проекте.
#### `git push`
Выгрузка в удаленный репозиторий подготовленного набора проиндексированных изменений из отслеживаемой локальной копии.
#### `git pull`
Загрузка содержимого из удаленного репозитория и немедленное слияние изменений в локальный отслеживаемый репозиторий.
#### `git restore`
Отмена индексации изменений в локальном репозитории.
#### `git revert`
Отмена подготовленных к отправке проиндексированных изменений в локальном репозитории с сохранением истории.
#### `git merge`
Слияние ответвления (ветки ответвленной с веткой изначальной). Изменения часто проходят процедуру согласования с участниками команды разработки через механизм merge Request.
#### `git rebase`
Операция, подобная слиянию (Git merge).
**Команды Git merge и Git rebase решают одну и ту же проблему –** **слияние одной ветки в другую, но делают это по-разному.**
**Git merge** — это неразрушающая операция, существующие ветки никак не изменяются.
![](https://legacy.merionet.ru/images/devops/images/3756/img_04.png)
**Git rebase** — это операция фактического перебазирования ответвленной ветки в исходную, с созданием идеальной линейной истории проекта.
![](https://legacy.merionet.ru/images/devops/images/3756/img_03.png)
Золотое правило перебазирования:
**“Never rebase while you're on a public branch”** (никогда не используйте git rebase в публичных репозиториях) Если вы предпочитаете иметь чистую линейную историю без ненужных коммитов слияния – используйте Git rebase.
Если вам необходимо сохранить полную историю проекта и избежать перезаписи публичных коммитов – используйте команду Git merge.
View File
View File
View File
@@ -7,20 +7,23 @@ tags:
- мифи - мифи
- разработка - разработка
created: 2026-06-08 created: 2026-06-08
updated: 2026-06-08 updated: 2026-06-14
--- ---
# УИР\_Сводный\_вариант # УИР\_Сводный\_вариант
## Введение ## Введение
Цифровизация управления учебным процессом требует от университета не только электронного хранения данных, но и прозрачной, проверяемой фиксации факта присутствия студентов на занятиях. В крупном техническом вузе, таком как НИЯУ МИФИ, традиционные бумажные журналы и разрозненные электронные таблицы создают существенные организационные ограничения: увеличивают нагрузку на преподавателей, повышают риск потери или несвоевременного внесения данных, затрудняют оперативное получение статистики деканатами и не позволяют автоматически синхронизировать сведения с внутренними информационными системами университета. Цифровизация управления учебным процессом требует от университета не только электронного хранения данных, но и прозрачной, проверяемой фиксации факта присутствия студентов на занятиях. Согласно исследованию [\[Электронный журнал как элемент цифровой трансформации вуза\]](https://ivdon.ru/back_media/uploads/article/pdf/IVD_47__4_glukhovsky_pirozhkov_tsvelik.pdf_048169e65f.pdf) средний балл студента обратно зависит от процента пропущенных им занятий. В крупном техническом вузе, традиционные бумажные журналы и разрозненные электронные таблицы создают существенные организационные ограничения: увеличивают нагрузку на преподавателей, повышают риск потери или несвоевременного внесения данных, затрудняют оперативное получение статистики деканатами и не позволяют автоматически синхронизировать сведения с внутренними информационными системами университета.
Проблема особенно актуальна для НИЯУ МИФИ, поскольку университет располагает развитой корпоративной IT-инфраструктурой: единой системой аутентификации, внутренними академическими реестрами, расписанием и ролевыми моделями доступа. Готовая система учёта посещаемости для такого окружения должна не просто фиксировать отметку, а работать как часть корпоративного контура: использовать существующие учётные записи, корректно разделять роли студентов, преподавателей, кураторов, деканата и администраторов, а также гарантированно передавать события посещаемости во внешние реестры. Проблема особенно актуальна для НИЯУ МИФИ, поскольку университет располагает развитой корпоративной IT-инфраструктурой: единой системой аутентификации, внутренними академическими реестрами, расписанием и ролевыми моделями доступа. Готовая система учёта посещаемости для такого окружения должна не просто фиксировать отметку, а работать как часть корпоративного контура: использовать существующие учётные записи, корректно разделять роли студентов, преподавателей, кураторов, деканата и администраторов, а также гарантированно передавать события посещаемости во внешние реестры.
Дополнительную актуальность задаче придаёт требование прозрачности. Студент должен иметь возможность видеть собственную историю посещений до окончания семестра; преподаватель должен иметь быстрый механизм фиксации присутствия и возможность ручной корректировки спорных случаев; куратор и деканат должны получать сводную аналитику без ручной консолидации журналов. Дополнительную актуальность задаче придаёт требование прозрачности:
1) студент должен иметь возможность видеть собственную историю посещений до окончания семестра;
2) преподаватель должен иметь быстрый механизм фиксации присутствия и возможность ручной корректировки спорных случаев;
3) куратор и деканат должны получать сводную аналитику без ручной консолидации журналов.
Проведённый анализ показывает, что массовые российские и зарубежные решения не сочетают одновременно ключевые свойства, необходимые корпоративному вузу: QR-самоотметку студента на очном занятии, интеграцию с корпоративным SSO на базе CAS, гарантированную синхронизацию с внутренними реестрами и полноценную вузовскую ролевую модель. Это обосновывает необходимость разработки собственной системы "Attendance MEPhI". Проведённый анализ показывает, что массовые российские и зарубежные решения не сочетают одновременно ключевые свойства, необходимые корпоративному вузу: QR-самоотметку студента на очном занятии, интеграцию с корпоративным SSO на базе CAS, гарантированную синхронизацию с внутренними реестрами и полноценную вузовскую ролевую модель. Это обосновывает необходимость разработки собственной системы.
**Целью** настоящей работы является определение подхода к разработке веб-приложения для автоматизированного учёта посещаемости студентов НИЯУ МИФИ, обеспечивающего прозрачную фиксацию присутствия на занятиях, интеграцию с корпоративными системами университета и надёжную синхронизацию данных с внешними реестрами. **Целью** настоящей работы является определение подхода к разработке веб-приложения для автоматизированного учёта посещаемости студентов НИЯУ МИФИ, обеспечивающего прозрачную фиксацию присутствия на занятиях, интеграцию с корпоративными системами университета и надёжную синхронизацию данных с внешними реестрами.
@@ -38,23 +41,22 @@ updated: 2026-06-08
**Методологическую основу** исследования составляет системный анализ существующих программных решений и технологических подходов в области автоматизации учёта посещаемости. В работе используются сравнительный анализ коммерческих и открытых систем, анализ методов фиксации посещаемости, обзор научных публикаций и проверка технических решений по официальной документации. **Методологическую основу** исследования составляет системный анализ существующих программных решений и технологических подходов в области автоматизации учёта посещаемости. В работе используются сравнительный анализ коммерческих и открытых систем, анализ методов фиксации посещаемости, обзор научных публикаций и проверка технических решений по официальной документации.
**Практическая значимость** работы заключается в формировании обоснованной методической базы для последующей разработки системы "Attendance MEPhI". Результаты анализа позволяют избежать типовых ошибок: зависимости от внешнего SaaS-провайдера, отсутствия гарантий доставки данных, невозможности интеграции с корпоративным SSO и смешения ролей пользователей. **Практическая значимость** работы заключается в формировании обоснованной методической базы для последующей разработки собственной системы. Результаты анализа позволяют избежать типовых ошибок: зависимости от внешнего SaaS-провайдера, отсутствия гарантий доставки данных, невозможности интеграции с корпоративным SSO и смешения ролей пользователей.
---
## 1. Обзор существующих систем учёта посещаемости студентов ## 1. Обзор существующих систем учёта посещаемости студентов
Перед проектированием новой системы необходимо оценить существующие решения: российские электронные журналы и ERP-платформы, зарубежные LMS и сервисы видеоконференций, специализированные QR/SaaS-сервисы и открытые прототипы. Цель анализа — определить, можно ли закрыть требования НИЯУ МИФИ готовым продуктом либо разработка собственной системы является обоснованной. Перед проектированием новой системы необходимо оценить существующие решения: российские электронные журналы и ERP-платформы, зарубежные LMS и сервисы видеоконференций, специализированные QR/SaaS-сервисы и открытые прототипы.
Цель анализа — определить, можно ли закрыть требования НИЯУ МИФИ готовым продуктом либо разработка собственной системы является обоснованной.
### 1.1. Российские системы ### 1.1. Российские системы
**Система "Учёт посещаемости студентов" ВВГУ.** Это внутренняя разработка Департамента цифрового развития Владивостокского государственного университета. По описанию ВВГУ, преподаватель во время занятия предоставляет студентам QR-код, при сканировании которого фиксируется присутствие; также возможна ручная отметка преподавателем. Система близка к целевому сценарию по способу фиксации, но является закрытым внутренним решением одного вуза. Для НИЯУ МИФИ она не закрывает требования интеграции с CAS, внешними академическими реестрами и собственной ролевой моделью. **Система «Учёт посещаемости студентов» ВВГУ.** Это внутренняя разработка Департамента цифрового развития Владивостокского государственного университета. По описанию ВВГУ, преподаватель во время занятия предоставляет студентам QR-код, при сканировании которого фиксируется присутствие; также возможна ручная отметка преподавателем. Система близка к целевому сценарию по способу фиксации, но является закрытым внутренним решением одного вуза. Для НИЯУ МИФИ она не закрывает требования интеграции с CAS, внешними академическими реестрами и собственной ролевой моделью. [Источник](https://www.vvsu.ru/news/201301/)
**БАРС.Образование Электронная школа.** Платформа ориентирована на электронный журнал и дневник школьного сегмента: оценки, темы уроков, расписание, домашние задания, посещаемость и отчётность. Ключевое ограничение состоит в самой модели предметной области: "школа — класс — родитель" плохо переносится на вузовскую структуру "поток — группа — подгруппа — кафедра — деканат". Посещаемость в таких системах, как правило, фиксируется учителем вручную; студенческая QR-самоотметка на очном занятии и интеграция с CAS вуза не являются базовым сценарием. **БАРС.Образование / ЭЖД Мос.ру** Платформы ориентированы на электронный журнал и дневник школьного сегмента: оценки, темы уроков, расписание, домашние задания, посещаемость и отчётность. Ключевое ограничение состоит в самой модели предметной области: «школа — класс — родитель» плохо переносится на вузовскую структуру «поток — группа — подгруппа — кафедра — деканат». Посещаемость в таких системах, как правило, фиксируется учителем вручную; студенческая QR-самоотметка на очном занятии и интеграция с CAS вуза не являются базовым сценарием. [Источник](https://барс-образование.рф/about/)
**1С:Университет.** Решение на платформе "1С:Предприятие 8.3" предназначено для комплексной автоматизации процессов вуза: приёмной кампании, учебных планов, контингента, нагрузки преподавателей, успеваемости и посещаемости. Его сильная сторона — широкий охват административных процессов. Ограничение для данной работы — избыточность ERP-подхода и зависимость от экосистемы 1С. Для задачи точечной разработки веб-системы QR-самоотметки с CAS и REST/Outbox-синхронизацией такое решение не является оптимальным. **1С:Университет.** Решение на платформе «1С:Предприятие 8.3» предназначено для комплексной автоматизации процессов вуза: приёмной кампании, учебных планов, контингента, нагрузки преподавателей, успеваемости и посещаемости. Его сильная сторона — широкий охват административных процессов. Ограничение для данной работы — избыточность ERP-подхода и зависимость от экосистемы 1С. Для задачи точечной разработки веб-системы QR-самоотметки с CAS и REST/Outbox-синхронизацией такое решение не является оптимальным.
**iSpring LMS (ранее iSpring Learn).** Платформа ориентирована на корпоративное обучение: онлайн-курсы, тесты, тренинги, отчёты, API, SSO и on-premise-развёртывание. Функции посещаемости привязаны к тренингам и корпоративным мероприятиям, а не к вузовскому расписанию очных занятий. Система не предоставляет целевую модель "поток — группа — занятие — преподаватель — студент" и не решает задачу гарантированной синхронизации посещаемости с академическими реестрами НИЯУ МИФИ. **iSpring LMS (ранее iSpring Learn).** Платформа ориентирована на корпоративное обучение: онлайн-курсы, тесты, тренинги, отчёты, API, SSO и on-premise-развёртывание. Функции посещаемости привязаны к тренингам и корпоративным мероприятиям, а не к вузовскому расписанию очных занятий. Система не предоставляет целевую модель «поток — группа — занятие — преподаватель — студент» и не решает задачу гарантированной синхронизации посещаемости с академическими реестрами НИЯУ МИФИ.
**Дневник.ру, ЭлЖур, Сетевой город. Образование.** Эти решения также относятся преимущественно к школьному электронному журналу. Их базовые сценарии строятся вокруг класса, учителя, родителей и ручной отметки посещаемости. Для корпоративного технического университета они не закрывают требования по CAS, QR-самоотметке, вузовской ролевой модели и on-premise-контролю данных. **Дневник.ру, ЭлЖур, Сетевой город. Образование.** Эти решения также относятся преимущественно к школьному электронному журналу. Их базовые сценарии строятся вокруг класса, учителя, родителей и ручной отметки посещаемости. Для корпоративного технического университета они не закрывают требования по CAS, QR-самоотметке, вузовской ролевой модели и on-premise-контролю данных.
@@ -66,33 +68,25 @@ updated: 2026-06-08
**Brightspace (D2L).** Brightspace содержит инструмент Attendance, позволяющий создавать реестры посещаемости и отмечать статусы студентов. По своей природе базовый инструмент является электронным журналом внутри LMS. QR-самоотметка возможна через сторонние интеграции, например Qwickly Attendance, но это требует внедрения внешнего модуля и всей LMS-среды. Для НИЯУ МИФИ такой путь избыточен и не решает нативную интеграцию с CAS и внутренними реестрами. **Brightspace (D2L).** Brightspace содержит инструмент Attendance, позволяющий создавать реестры посещаемости и отмечать статусы студентов. По своей природе базовый инструмент является электронным журналом внутри LMS. QR-самоотметка возможна через сторонние интеграции, например Qwickly Attendance, но это требует внедрения внешнего модуля и всей LMS-среды. Для НИЯУ МИФИ такой путь избыточен и не решает нативную интеграцию с CAS и внутренними реестрами.
**Специализированные QR/чек-ин SaaS-сервисы.** Сервисы наподобие OneTap, AccuClass и аналогичных решений предоставляют QR-чек-ин, импорт списков и отчёты. Их ограничение — ориентация на мероприятия, учебные центры или облачный сценарий. Для государственного вуза это создаёт риски размещения персональных данных у внешнего поставщика, vendor lock-in и зависимость от модели данных провайдера.
**Открытые прототипы на GitHub.** В открытом доступе есть студенческие и демонстрационные QR Attendance-проекты на Django, PHP, Java, .NET и других стеках. Они полезны как технические примеры, но не обладают промышленной надёжностью, полноценной ролевой моделью вуза, CAS-интеграцией и механизмом гарантированной доставки событий во внешние системы.
### 1.3. Сравнительный анализ аналогов ### 1.3. Сравнительный анализ аналогов
Сравнительная оценка систем по критериям, важным для НИЯУ МИФИ, представлена в Таблице 1. Сравнительная оценка систем по критериям, важным для НИЯУ МИФИ, представлена в Таблице 1.
| Система | QR-self-service | SSO/CAS | Outbox-синхронизация | Ролевая модель вуза | On-premise / контроль данных | Контроль исходного кода | Итог | | Система | QR-self-service | SSO/CAS | Ролевая модель вуза | On-premise / контроль данных | Итог |
|---|:---:|:---:|:---:|:---:|:---:|:---:|---| | ------------------------ | :--------------: | :-------------------: | :-----------------: | :--------------------------: | ------------------------------------------------- |
| ВВГУ "Учёт посещаемости" | ✔ | ✘ | ✘ | Частично | ✔ | ✘ | Не переносится в МИФИ | | ВВГУ «Учёт посещаемости» | + | - | Частично | + | Не переносится в МИФИ |
| БАРС.Образование | ✘ | ✘ | ✘ | Школьная | Зависит от внедрения | ✘ | Не подходит | | БАРС.Образование | - | - | Школьная | Зависит от внедрения | Не подходит в силу нацеленности на школьный фрмат |
| 1С:Университет | ✘ | ✘ | ✘ | ✔ | ✔ | ✘ | Избыточная ERP-платформа | | 1С:Университет | - | - | + | + | Избыточная ERP-платформа |
| iSpring LMS | ✘ | SSO, не CAS МИФИ | ✘ | Корпоративная | ✔ | ✘ | Не подходит для вузовской QR-самоотметки | | iSpring LMS | - | SSO, не интегрируется | Корпоративная | + | Не подходит для вузовской QR-самоотметки |
| Microsoft Teams | ✘ | Azure AD | ✘ | ✘ | ✘ | ✘ | Только онлайн-встречи | | Microsoft Teams | - | Azure AD | - | - | Только онлайн-встречи |
| Google Meet/Classroom | ✘ | Google | ✘ | ✘ | ✘ | ✘ | Только онлайн-встречи | | Google Meet/Classroom | - | Google | - | - | Только онлайн-встречи |
| Brightspace (D2L) | Через интеграции | Зависит от внедрения | ✘ | LMS | Зависит от контракта | ✘ | Избыточно | | Brightspace (D2L) | Через интеграции | Зависит от внедрения | LMS | Зависит от контракта | Избыточно |
| QR-SaaS-сервисы | ✔ | Частично | ✘ | ✘ | ✘ | ✘ | Риски ПДн и vendor lock-in | | **Attendance MEPhI** | **+** | **(CAS МИФИ)** | **+** | **+** | **Целевое решение** |
| Открытые прототипы | ✔ | ✘ | ✘ | ✘ | ✔ | ✔ | Не production-grade |
| **Attendance MEPhI** | **✔** | **✔ (CAS МИФИ)** | **✔** | **✔** | **✔** | **✔** | **Целевое решение** |
*Таблица 1 — Сравнительная оценка систем учёта посещаемости* *Таблица 1 — Сравнительная оценка систем учёта посещаемости*
Вывод по разделу: готового продукта, который одновременно обеспечивает QR-самоотметку очной посещаемости, интеграцию с CAS МИФИ, гарантированную синхронизацию с внутренними реестрами, полную вузовскую ролевую модель и контроль данных на стороне университета, не выявлено. Следовательно, разработка собственной системы является обоснованной. Вывод по разделу: готового продукта, который одновременно обеспечивает QR-самоотметку очной посещаемости, интеграцию с CAS МИФИ, гарантированную синхронизацию с внутренними реестрами, полную вузовскую ролевую модель и контроль данных на стороне университета, не выявлено. Следовательно, разработка собственной системы является обоснованной.
---
## 2. Анализ методов фиксации посещаемости студентов ## 2. Анализ методов фиксации посещаемости студентов
Для выбора основного механизма фиксации посещаемости необходимо сравнить возможные методы по инфраструктурным затратам, скорости, достоверности, правовым рискам и применимости в условиях многокорпусного университета. Для выбора основного механизма фиксации посещаемости необходимо сравнить возможные методы по инфраструктурным затратам, скорости, достоверности, правовым рискам и применимости в условиях многокорпусного университета.
@@ -103,21 +97,24 @@ updated: 2026-06-08
**QR-коды.** Преподаватель генерирует QR-код для конкретного занятия, студент сканирует его смартфоном и проходит отметку через аутентифицированную сессию. Метод не требует установки считывателей в аудиториях, быстро внедряется и естественно связывается с расписанием, преподавателем и конкретной парой. Защита от злоупотреблений обеспечивается ограниченным временем жизни QR-кода, одноразовым токеном и проверкой личности через CAS. **QR-коды.** Преподаватель генерирует QR-код для конкретного занятия, студент сканирует его смартфоном и проходит отметку через аутентифицированную сессию. Метод не требует установки считывателей в аудиториях, быстро внедряется и естественно связывается с расписанием, преподавателем и конкретной парой. Защита от злоупотреблений обеспечивается ограниченным временем жизни QR-кода, одноразовым токеном и проверкой личности через CAS.
**Биометрическая идентификация.** Биометрия обеспечивает высокий уровень достоверности, но создаёт существенные правовые и организационные риски. Обработка биометрических персональных данных в РФ регулируется статьёй 11 Федерального закона № 152-ФЗ "О персональных данных" и требует отдельного правового обоснования. Для учебного проекта и массового внедрения в аудиториях этот метод избыточен. **СКУД.** Системы контроля и управления доступов исторически являлись одним из самых надежных способов фиксации посещения, но использование их, как единственного метода — недостаточно, так как проход через турникет не гарантирует присутствие на лекции, но целесообразно использовать такие системы, как дополнительную проверку.
**Биометрическая идентификация.** Биометрия обеспечивает высокий уровень достоверности, но создаёт существенные правовые и организационные риски. Обработка биометрических персональных данных в РФ регулируется статьей 11 Федерального закона № 152-ФЗ «О персональных данных» и требует отдельного правового обоснования. Для учебного проекта и массового внедрения в аудиториях этот метод избыточен.
**Геолокация.** Геолокационная отметка не требует аудиторного оборудования, но точности GPS и Wi-Fi-позиционирования может быть недостаточно для различения смежных аудиторий. Кроме того, постоянная или регулярная обработка местоположения студентов повышает чувствительность решения с точки зрения приватности. **Геолокация.** Геолокационная отметка не требует аудиторного оборудования, но точности GPS и Wi-Fi-позиционирования может быть недостаточно для различения смежных аудиторий. Кроме того, постоянная или регулярная обработка местоположения студентов повышает чувствительность решения с точки зрения приватности.
| Метод | Инфраструктура | Скорость | Защита от подмены | Правовые риски | Применимость в МИФИ | | Метод | Инфраструктура | Скорость | Защита от подмены | Правовые риски | Применимость в МИФИ |
|---|---|---|---|---|---| | -------------- | ------------------------------- | ------------------------- | ----------------------------- | -------------- | -------------------------------------- |
| Ручная отметка | Минимальная | Низкая на больших потоках | Низкая | Минимальные | Резервный сценарий | | Ручная отметка | Минимальная | Низкая на больших потоках | Низкая | Минимальные | Резервный сценарий |
| RFID/NFC | Высокие затраты на считыватели | Высокая | Средняя | Низкие | Ограниченная | | RFID/NFC | Высокие затраты на считыватели | Высокая | Средняя | Низкие | Ограниченная |
| **QR-коды** | **Минимальная** | **Высокая** | **Средняя при токенах и CAS** | **Низкие** | **Оптимальная** | | **QR-коды** | **Минимальная** | **Высокая** | **Средняя при токенах и CAS** | **Низкие** | **Оптимальная** |
| Биометрия | Высокие затраты на оборудование | Высокая | Высокая | Высокие | Не рекомендуется | | СКУД | Затраты на оборудование | Высокая | Высокая | Низкие | Уже используется (возможна интеграция) |
| Геолокация | Минимальная | Высокая | Низкая/средняя | Средние | Вспомогательная | | Биометрия | Высокие затраты на оборудование | Высокая | Высокая | Высокие | Не рекомендуется |
| Геолокация | Минимальная | Высокая | Низкая/средняя | Средние | Усложненная |
*Таблица 2 — Сравнительная оценка методов фиксации посещаемости* *Таблица 2 — Сравнительная оценка методов фиксации посещаемости*
Основным методом для "Attendance MEPhI" целесообразно выбрать QR-коды. Ручная отметка преподавателем должна сохраниться как резервный механизм для спорных ситуаций, технических сбоев и студентов без доступа к смартфону в момент занятия. Основным методом для системы целесообразно выбрать QR-коды. Ручная отметка преподавателем должна сохраниться как резервный механизм для спорных ситуаций, технических сбоев и студентов без доступа к смартфону в момент занятия.
--- ---
@@ -163,8 +160,6 @@ updated: 2026-06-08
PostgreSQL особенно важен для реализации Transactional Outbox: отметка посещаемости и запись события синхронизации должны выполняться атомарно. Если запись отметки прошла успешно, но событие не было создано, внешние реестры могут остаться несогласованными. Если событие было отправлено без успешной записи отметки, возникнет обратная ошибка. Транзакционная модель PostgreSQL позволяет избежать этих сценариев. PostgreSQL особенно важен для реализации Transactional Outbox: отметка посещаемости и запись события синхронизации должны выполняться атомарно. Если запись отметки прошла успешно, но событие не было создано, внешние реестры могут остаться несогласованными. Если событие было отправлено без успешной записи отметки, возникнет обратная ошибка. Транзакционная модель PostgreSQL позволяет избежать этих сценариев.
Сравнение с MySQL не должно строиться только на тезисе "PostgreSQL быстрее". Корректнее указать, что в цитируемом бенчмарке Salunke и Ouda PostgreSQL показал сильные результаты на ряде операций, а выбор для данного проекта дополнительно обоснован транзакционной целостностью и возможностями типов данных.
### 4.4. Фоновые задачи: Sidekiq и Redis ### 4.4. Фоновые задачи: Sidekiq и Redis
Для фоновой обработки выбран **Sidekiq** с **Redis** в качестве хранилища очередей. Sidekiq использует многопоточную модель и хорошо интегрируется с Rails-приложениями. Для фоновой обработки выбран **Sidekiq** с **Redis** в качестве хранилища очередей. Sidekiq использует многопоточную модель и хорошо интегрируется с Rails-приложениями.
View File
View File
+224
View File
@@ -0,0 +1,224 @@
---
title: CI/CD — основы
status: processing
type: concept
tags:
- devops
- cicd
- gitlab-ci
- jenkins
created: 2026-06-12
updated: 2026-06-16
aliases:
- Основы CI/CD
- Непрерывная интеграция и доставка
source:
- "CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins"
- "[[CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов]]"
---
# CI/CD — Основы
**CI/CD** — набор практик автоматизации интеграции, проверки, сборки и доставки изменений программного продукта. Он сокращает цикл обратной связи и делает выпуск изменений повторяемым.
CI/CD связывает управление кодом через [[Git]], командную работу в платформах вроде [[Gitlab]] и развёртывание продукта в целевой инфраструктуре. Это одна из базовых практик [[DevOps - основы]].
## Состав CI/CD
### Continuous Integration
**Continuous Integration (CI)** — частое объединение небольших изменений в общую кодовую базу с автоматической проверкой каждого изменения.
Типичный CI-процесс:
1. Разработчик отправляет изменения в репозиторий.
2. Система запускает статический анализ и автоматические тесты.
3. Приложение собирается или компилируется.
4. Результат упаковывается в версионированный артефакт.
5. Артефакт сохраняется в реестре или хранилище.
Unit-тесты запускают отдельные единицы кода в контролируемом окружении. Кроме них в CI могут входить линтеры, SAST, SCA, интеграционные тесты и проверки качества.
### Continuous Delivery
**Continuous Delivery** — поддержание проверенного артефакта в состоянии готовности к выпуску. Доставка до production обычно требует явного решения или ручного подтверждения.
![[Pasted image 20260612125256.png|353]]
### Continuous Deployment
**Continuous Deployment** — автоматическое развёртывание каждого изменения, прошедшего все проверки, в production без ручного подтверждения.
Delivery и deployment часто обозначают одной аббревиатурой CD, но уровень автоматизации выпуска у них различается.
![[Pasted image 20260612125409.png]]
## Пайплайн
**Pipeline** — описанная последовательность стадий и задач CI/CD. Обычно новый запуск инициируется коммитом, merge request, тегом, расписанием или ручным действием.
Распространённые стадии:
1. получение исходного кода;
2. линтинг и статический анализ;
3. модульное и интеграционное тестирование;
4. сборка приложения;
5. упаковка артефакта, например контейнерного образа на основе [[Основы - Docker]];
6. публикация артефакта;
7. развёртывание;
8. дополнительные проверки;
9. сбор метрик и обратной связи.
Отдельная задача внутри pipeline обычно называется **job**. Jobs объединяются в stages и выполняются на выделенных исполнителях.
Stages задают крупные фазы процесса, например `build`, `test`, `deploy`. Jobs внутри разных stages обычно выполняются последовательно по порядку стадий, а несколько jobs внутри одной stage могут выполняться параллельно, если есть свободные исполнители.
Минимальный пример `.gitlab-ci.yml`:
```yaml
stages:
- build
- test
- deploy
build-job:
stage: build
script:
- make build
test-job:
stage: test
script:
- make test
deploy-job:
stage: deploy
script:
- make deploy
```
## Артефакт
**Артефакт** — неизменяемый результат сборки, который можно проверить, хранить и продвигать между средами. Это может быть бинарный файл, пакет, архив или контейнерный образ.
Один и тот же проверенный артефакт желательно последовательно использовать во всех средах. Повторная сборка перед production создаёт риск получить результат, отличный от протестированного.
## Среды
Продукт проходит через несколько изолированных сред:
- **Dev** — разработка и ранняя интеграция;
- **Test / QA / Integration** — функциональные и интеграционные проверки;
- **Stage / Pre-production / UAT** — проверка в окружении, близком к production;
- **Prod** — эксплуатация продукта пользователями.
Развёртывание может выполняться на виртуальных машинах, в контейнерах или в оркестраторе, например [[Kubernetes - MOC]].
## Обратная связь
Pipeline не заканчивается фактом развёртывания. После выпуска необходимо собирать:
- метрики приложения и инфраструктуры;
- логи;
- трассировки;
- сведения об ошибках;
- продуктовые и пользовательские метрики.
Эти данные возвращаются в цикл разработки и помогают оценить качество релиза. Наблюдаемость pipeline также показывает длительность задач, частоту сбоев и узкие места доставки.
## GitLab CI/CD
**GitLab CI/CD** — встроенная в [[Gitlab]] система автоматизации pipeline. Конфигурация обычно хранится рядом с кодом в файле `.gitlab-ci.yml`.
Jobs выполняются компонентами **GitLab Runner**. Runner может работать на физическом сервере, виртуальной машине, в Docker или Kubernetes.
Конфигурация GitLab CI/CD сочетает декларативный YAML и команды shell. YAML описывает stages, jobs, переменные, зависимости и условия запуска. Shell-команды внутри `script` выполняют конкретные действия: сборку, тесты, публикацию артефактов или деплой.
Pipeline можно запускать:
- вручную через веб-интерфейс или API;
- автоматически по push, commit, merge request, tag и другим событиям;
- через webhook или интеграцию с внешней системой;
- с параметрами: переменными, выбранной веткой, тегом, коммитом или окружением.
В сложных проектах pipeline могут быть составными: родительский pipeline запускает дочерние pipeline, а отдельные части процесса выполняются последовательно или параллельно. Это помогает разделять сборку, тестирование, деплой и проверки безопасности по независимым конфигурациям.
Преимущества:
- тесная интеграция с репозиториями, merge request и правами GitLab;
- pipeline as code;
- управление jobs, stages, артефактами и окружениями в одном интерфейсе;
- просмотр состояния и результатов задач;
- возможность масштабирования runners.
Ограничения:
- наиболее удобен внутри экосистемы GitLab;
- сложные pipeline требуют аккуратной организации YAML-конфигурации;
- поведение артефактов, cache и зависимостей между jobs необходимо явно проектировать.
## Jenkins
**Jenkins** — самостоятельный open-source сервер автоматизации. Pipeline можно описывать декларативно или программно в `Jenkinsfile` с использованием Groovy.
Для Jenkins важна экосистема плагинов: через них подключаются системы контроля версий, учётные данные, агенты, уведомления, Kubernetes, инструменты безопасности и observability. Плагины упрощают интеграции, но требуют регулярного обновления и контроля совместимости.
Pipeline можно настроить через веб-интерфейс, но для воспроизводимости лучше хранить `Jenkinsfile` в репозитории рядом с кодом. Тогда изменения pipeline проходят тот же контроль, что и изменения продукта: review, merge request, история коммитов и ограничения доступа.
Запуск Jenkins pipeline возможен:
- вручную через веб-интерфейс или API;
- автоматически через интеграции и плагины;
- через webhook из GitLab или другой VCS-платформы;
- по расписанию.
При описании pipeline на Groovy Jenkins может генерировать фрагменты синтаксиса через встроенный помощник. Это снижает риск ошибки при работе с параметрами шагов и плагинов.
Преимущества:
- большая экосистема плагинов;
- интеграция со множеством систем контроля версий, сборки и развёртывания;
- гибкость при построении нестандартных процессов;
- развитое сообщество и документация.
Ограничения:
- сервер и агенты необходимо отдельно устанавливать, обновлять и защищать;
- плагины увеличивают поверхность атаки и стоимость сопровождения;
- конфликты версий и накопление плагинов усложняют обновления;
- для небольших команд эксплуатация может быть тяжелее встроенного решения.
## Развитие CI/CD
CI/CD внедряют поэтапно: сначала автоматизируют сборку и базовые проверки, затем добавляют доставку артефактов, деплой, наблюдаемость и проверки безопасности. Процесс не должен останавливаться после первого рабочего pipeline.
Практики, которые усиливают CI/CD:
- [[IaC - основы]] — декларативное описание инфраструктуры и конфигурации;
- контейнеризация через [[Основы - Docker]] и оркестрация через [[Kubernetes - MOC]];
- observability: метрики, логи, трассировки, аудит, обработка логов и обратная связь;
- DevSecOps-проверки: SAST, SCA, DAST, IAST, RASP, сканирование инфраструктуры и работа с секретами.
Эти проверки можно добавлять как отдельные stages или jobs. Главное - не превращать pipeline в непрозрачный набор ручных действий: сборка, тесты, доставка и контроль качества должны быть описаны как код и воспроизводиться одинаково.
## GitLab CI/CD и Jenkins
| Критерий | GitLab CI/CD | Jenkins |
|---|---|---|
| Модель | Встроен в GitLab | Самостоятельный сервер |
| Описание pipeline | YAML | Jenkinsfile, Groovy |
| Исполнители | GitLab Runner | Jenkins agents |
| Интеграции | Нативные возможности GitLab | Обширная система плагинов |
| Сопровождение | Зависит от способа размещения GitLab и runners | Требует сопровождения controller, agents и плагинов |
| Основной сценарий | Проекты, уже использующие GitLab | Гетерогенная инфраструктура и нестандартные интеграции |
Если код и процессы команды находятся в GitLab, GitLab CI/CD обычно требует меньше интеграционной работы. Jenkins оправдан, когда важны независимость от одной платформы, существующая Jenkins-инфраструктура или специфические интеграции.
## Связанные заметки
- [[DevOps - основы]]
- [[Git]]
- [[Gitlab]]
- [[IaC - основы]]
- [[Основы - Docker]]
- [[Kubernetes - MOC]]
@@ -6,11 +6,12 @@ tags:
- infrastructure - infrastructure
- devops - devops
created: 2026-05-18 created: 2026-05-18
updated: 2026-05-18 updated: 2026-06-15
source: '[[Infrastructure]]' source: '[[Infrastructure]]'
aliases: aliases:
- Infrastructure — ada-dev - Infrastructure — ada-dev
--- ---
# Infrastructure — ada-dev # Infrastructure — ada-dev
Заметка фиксирует текущую схему homelab-инфраструктуры `ada-dev`: публичный VPS, домашние узлы, reverse proxy, overlay-сеть и пользовательские сервисы. Заметка фиксирует текущую схему homelab-инфраструктуры `ada-dev`: публичный VPS, домашние узлы, reverse proxy, overlay-сеть и пользовательские сервисы.
@@ -29,7 +30,7 @@ VPS принимает внешний трафик и проксирует ег
## Узлы ## Узлы
### ru-vps ### Ru-vps
`ru-vps` — публичный сервер с белым IP. На схеме он выполняет роль внешнего шлюза: `ru-vps` — публичный сервер с белым IP. На схеме он выполняет роль внешнего шлюза:
@@ -41,7 +42,7 @@ VPS принимает внешний трафик и проксирует ег
Практический смысл VPS — держать наружу только минимальный набор портов и не раскрывать домашнюю сеть напрямую. Практический смысл VPS — держать наружу только минимальный набор портов и не раскрывать домашнюю сеть напрямую.
### mini-pc ### Mini-pc
`mini-pc` описан как `N150 / 12 GB / 256 GB`. По схеме это небольшой домашний узел для инфраструктурных сервисов: `mini-pc` описан как `N150 / 12 GB / 256 GB`. По схеме это небольшой домашний узел для инфраструктурных сервисов:
@@ -53,7 +54,7 @@ VPS принимает внешний трафик и проксирует ег
Логика узла: локальная инфраструктура и сервисы, которым полезно быть рядом с домашней сетью. Логика узла: локальная инфраструктура и сервисы, которым полезно быть рядом с домашней сетью.
### cloud-pc ### Cloud-pc
`cloud-pc` описан как `i5 / 16 GB / 256 GB + 5 TB`. Это более крупный узел для сервисов и хранения: `cloud-pc` описан как `i5 / 16 GB / 256 GB + 5 TB`. Это более крупный узел для сервисов и хранения:
@@ -0,0 +1,99 @@
---
title: Proxmox VE — local, local-lvm, LVM
status: seed
type: guide
tags:
- homelab
- proxmox
- lvm
- storage
created: 2025-12-17
updated: 2026-06-15
aliases: []
source: "[[Proxmox VE — local, local-lvm, LVM — source]]"
---
# Proxmox VE — local, local-lvm, LVM
## Что такое local и local-lvm
|Storage|Тип|Путь / устройство|Хранит|
|---|---|---|---|
|`local`|directory|`/var/lib/vz`|ISO, CT-шаблоны, бэкапы, snippets|
|`local-lvm`|LVM thin pool|`pve/data` (LV в VG `pve`)|Диски VM/CT, снапшоты дисков|
## Куда скачиваются ISO через веб-интерфейс
По умолчанию — в `local` (`/var/lib/vz/template/iso/`).
Чтобы скачивать на другой storage — выбрать его в выпадающем списке при Download from URL.
Условие: у storage должен быть включён Content type **ISO image** (Datacenter → Storage → Edit → Content).
## Проверить содержимое LVM
```bash
# Список всех LV с размерами
lvs
# Storage Proxmox с backing-устройствами
pvesm status
```
Thin pool определяется по атрибуту `twi` в колонке `Attr`.
## Удалить local-lvm и расширить root
> Перед удалением убедиться, что на `local-lvm` нет дисков VM/CT (Data% = 0.00).
### 1. Удалить thin pool
```bash
sudo lvremove /dev/pve/data
# подтвердить: y
```
### 2. Расширить root на нужный размер
```bash
# Добавить конкретный размер (например, 60G):
sudo lvresize -L +60G /dev/pve/root
# Или отдать всё свободное место:
sudo lvresize -l +100%FREE /dev/pve/root
```
### 3. Расширить файловую систему
```bash
sudo resize2fs /dev/pve/root
```
Работает online, перезагрузка не нужна.
### 4. Удалить storage из GUI
Datacenter → Storage → `local-lvm` → Remove
### 5. Проверить результат
```bash
lsblk
lvs
df -h /
```
## Что делать с оставшимся свободным местом в VG pve
Нераспределённое пространство можно использовать:
- добавить как новый LVM storage в Proxmox
- создать новый LV под конкретную задачу
- оставить в резерве
```bash
# Посмотреть свободное место в VG:
vgs
```
## Связанные заметки
- [[Infrastructure — ada-dev]]
+403
View File
@@ -0,0 +1,403 @@
---
title: Sudoers
status: seed
type: guide
tags:
- linux
- sudo
- security
created: 2026-06-15
updated: 2026-06-15
aliases:
- sudoers
- Настройка sudo
source: "[[Sudoers — source]]"
---
# Sudoers
`sudo` запускает команду с полномочиями другого пользователя, обычно `root`. Политика доступа задаётся в `/etc/sudoers` и дополнительных файлах из `/etc/sudoers.d/`.
`sudo` не является заменой файловых прав. Он выдаёт полномочия на конкретное действие после проверки пользователя, хоста, целевого пользователя, команды и параметров политики.
## Где хранится конфигурация
- `/etc/sudoers` — основной файл.
- `/etc/sudoers.d/` — каталог для отдельных правил.
- `/var/log/` или журнал systemd — записи о вызовах `sudo`, в зависимости от конфигурации системы.
Основной файл обычно содержит:
```sudoers
@includedir /etc/sudoers.d
```
Для локальных правил удобнее создавать отдельный файл:
```bash
sudo visudo -f /etc/sudoers.d/backup
```
Имена файлов в `sudoers.d` лучше делать без точки и символа `~`: некоторые реализации игнорируют такие файлы. Права должны исключать запись обычными пользователями:
```bash
sudo chown root:root /etc/sudoers.d/backup
sudo chmod 0440 /etc/sudoers.d/backup
```
## Редактирование через visudo
Не следует редактировать `/etc/sudoers` обычным редактором. `visudo` блокирует файл от одновременного изменения и проверяет синтаксис перед сохранением.
```bash
sudo visudo
sudo visudo -f /etc/sudoers.d/backup
sudo visudo -c
```
Если ошибка уже внесена, исправление потребует действующей root-сессии, загрузки в recovery mode или другого способа получить root-доступ.
Редактор можно выбрать переменными окружения:
```bash
sudo EDITOR=nvim visudo
```
Возможность выбора редактора зависит от настроек `sudo`; небезопасные переменные окружения могут быть отброшены.
## Формат правила
Базовая форма:
```sudoers
пользователь хост=(целевой_пользователь:целевая_группа) теги: команды
```
Пример:
```sudoers
ada ALL=(root) /usr/bin/systemctl restart nginx
```
Значение полей:
| Поле | Смысл |
|---|---|
| `ada` | кому разрешено использовать правило |
| `ALL` | на каких хостах действует правило |
| `(root)` | от имени какого пользователя разрешён запуск |
| `/usr/bin/systemctl restart nginx` | разрешённая команда и аргументы |
Группа указывается с `%`:
```sudoers
%wheel ALL=(ALL:ALL) ALL
```
Это разрешает членам группы `wheel` запускать любые команды от имени любого пользователя и группы. Такое правило выдаёт полный административный доступ.
## Команды и аргументы
В правилах следует использовать абсолютный путь:
```sudoers
ada ALL=(root) /usr/bin/systemctl status nginx
```
Путь можно узнать командой:
```bash
command -v systemctl
```
Аргументы являются частью разрешения. Эти правила различаются:
```sudoers
ada ALL=(root) /usr/bin/systemctl restart nginx
ada ALL=(root) /usr/bin/systemctl restart *
```
Первое разрешает перезапустить только `nginx`. Второе намного шире и может разрешить перезапуск произвольного юнита.
Правило без аргументов в современных версиях `sudo` может разрешать команду с любыми аргументами. Для явного запрета аргументов используется пустая строка:
```sudoers
ada ALL=(root) /usr/bin/id ""
```
Поведение сопоставления аргументов и специальных символов зависит от версии `sudo`. После изменения правило нужно проверять на целевой системе.
## Псевдонимы
Псевдонимы уменьшают повторение в больших конфигурациях.
### User_Alias
```sudoers
User_Alias OPERATORS = ada, alice, %ops
```
### Runas_Alias
```sudoers
Runas_Alias SERVICE_USERS = nginx, postgres
```
### Host_Alias
```sudoers
Host_Alias WEB_SERVERS = web01, web02
```
### Cmnd_Alias
```sudoers
Cmnd_Alias NGINX_CONTROL = \
/usr/bin/systemctl status nginx, \
/usr/bin/systemctl reload nginx, \
/usr/bin/systemctl restart nginx
%ops WEB_SERVERS=(root) NGINX_CONTROL
```
Имена псевдонимов принято писать в верхнем регистре.
## Теги
### PASSWD и NOPASSWD
По умолчанию `sudo` запрашивает пароль вызывающего пользователя:
```sudoers
ada ALL=(root) /usr/bin/systemctl restart nginx
```
Без пароля:
```sudoers
ada ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
```
`NOPASSWD` удобно для автоматизации, но увеличивает последствия компрометации учётной записи. Его следует ограничивать минимальным набором команд.
Теги действуют на следующие команды в списке, пока не будут переопределены:
```sudoers
ada ALL=(root) NOPASSWD: /usr/bin/systemctl status nginx, \
PASSWD: /usr/bin/systemctl restart nginx
```
### NOEXEC
```sudoers
ada ALL=(root) NOEXEC: /usr/bin/less /var/log/nginx/error.log
```
`NOEXEC` пытается запретить программе запускать другие процессы. Это дополнительная мера, а не надёжная граница безопасности: поддержка зависит от платформы и типа бинарника.
### SETENV
```sudoers
ada ALL=(root) SETENV: /usr/local/sbin/deploy
```
`SETENV` позволяет передавать переменные окружения с меньшим числом ограничений. Это опасно для команд, поведение которых зависит от `PATH`, загрузчиков библиотек, интерпретаторов и конфигурационных переменных.
## Defaults
Директивы `Defaults` управляют поведением `sudo`.
```sudoers
Defaults env_reset
Defaults use_pty
Defaults timestamp_timeout=5
Defaults passwd_tries=3
```
Часто используемые параметры:
| Параметр | Назначение |
|---|---|
| `env_reset` | оставляет ограниченный набор переменных окружения |
| `secure_path` | задаёт доверенный `PATH` для команд через `sudo` |
| `use_pty` | запускает команду в псевдотерминале |
| `timestamp_timeout` | срок действия успешной аутентификации в минутах |
| `passwd_tries` | число попыток ввода пароля |
| `log_input`, `log_output` | запись ввода и вывода поддерживаемых сессий |
Пример `secure_path`:
```sudoers
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/bin"
```
Настройки можно ограничить пользователем, группой, хостом или командой:
```sudoers
Defaults:ada timestamp_timeout=0
Defaults:%ops use_pty
Defaults!/usr/bin/systemctl !log_output
```
`timestamp_timeout=0` требует пароль для каждого отдельного запуска. Отрицательное значение создаёт сессию без ограничения по времени и обычно нежелательно.
## Запуск от имени другого пользователя
```sudoers
ada ALL=(postgres) /usr/bin/psql
```
Использование:
```bash
sudo -u postgres /usr/bin/psql
```
С указанием группы:
```sudoers
ada ALL=(deploy:deploy) /usr/local/bin/release
```
## Запреты
Команду можно исключить через `!`:
```sudoers
ada ALL=(root) ALL, !/usr/bin/su, !/usr/bin/bash
```
Такой список не создаёт безопасное ограничение полного административного доступа. Пользователь с разрешением `ALL` часто может получить оболочку другим способом: через редактор, интерпретатор, отладчик, файловый менеджер, загрузку модуля, изменение исполняемого файла или сервисной конфигурации.
Отрицания полезны для уточнения узкого списка, но не должны использоваться как sandbox.
## Опасные категории команд
Нельзя считать ограниченными команды, которые позволяют:
- запустить оболочку или произвольный процесс;
- выполнять код или загружать модули;
- редактировать произвольные файлы;
- менять владельца, права или ACL;
- записывать в исполняемые файлы и каталоги из `PATH`;
- менять unit-файлы, cron-задачи, PAM, загрузчик или конфигурацию сервисов;
- читать секреты и закрытые ключи;
- управлять контейнерами или виртуальными машинами с доступом к хосту.
Примеры команд, которые часто позволяют выйти к root:
```text
sh, bash, zsh, python, ruby, perl, vim, nvim, less, find,
tar, rsync, awk, sed, env, systemctl, docker, podman
```
Риск зависит от разрешённых аргументов, файловой системы и окружения. Само имя команды недостаточно для оценки безопасности.
## Безопасная автоматизация
Предпочтительный подход — разрешить один root-owned wrapper с фиксированным поведением:
```sudoers
deploy ALL=(root) NOPASSWD: /usr/local/sbin/restart-myapp
```
Требования к wrapper:
- файл и родительские каталоги не доступны на запись вызывающему пользователю;
- используются абсолютные пути;
- входные параметры проверяются по allowlist;
- не выполняются строки через shell без необходимости;
- окружение очищается или задаётся явно;
- временные файлы создаются безопасно;
- логируются значимые действия.
Если пользователь может изменить разрешённый скрипт, библиотеку, конфигурацию или исполняемый файл, правило фактически разрешает выполнение произвольного кода с повышенными правами.
## Проверка правил
Показать доступные текущему пользователю команды:
```bash
sudo -l
```
Проверить другого пользователя от root:
```bash
sudo -l -U ada
```
Сбросить сохранённую аутентификацию:
```bash
sudo -k
```
Удалить timestamp полностью:
```bash
sudo -K
```
Проверить конфигурацию:
```bash
sudo visudo -c
```
Проверять нужно не только синтаксис, но и фактический результат `sudo -l`: правила могут объединяться, а более широкое разрешение в другом файле отменяет ожидаемое ограничение.
## Приоритет и объединение правил
`sudoers` не работает как обычный firewall с простым правилом «первое совпадение победило». Для пользователя могут совпасть несколько записей; разрешения и теги вычисляются по правилам sudoers и могут объединяться.
Практические следствия:
- узкое правило не отменяет широкое разрешение из другого файла;
- порядок важен для некоторых тегов и параметров;
- членство пользователя в нескольких административных группах нужно учитывать;
- проверять итог следует через `visudo -c` и `sudo -l`.
## Минимальный пример
Задача: разрешить группе `webops` проверять и перезапускать только `nginx`.
```sudoers
Cmnd_Alias NGINX_READ = \
/usr/bin/systemctl status nginx
Cmnd_Alias NGINX_WRITE = \
/usr/bin/systemctl reload nginx, \
/usr/bin/systemctl restart nginx
%webops ALL=(root) NGINX_READ
%webops ALL=(root) PASSWD: NGINX_WRITE
```
Проверка:
```bash
sudo visudo -c
sudo -l
sudo /usr/bin/systemctl status nginx
```
## Чек-лист
- Правило хранится в отдельном файле `/etc/sudoers.d/`.
- Файл проверен через `visudo`.
- Использованы абсолютные пути.
- Разрешены конкретные команды и аргументы.
- Нет лишнего `ALL`.
- `NOPASSWD` используется только при необходимости.
- Пользователь не может изменить разрешённую команду или её зависимости.
- Не разрешены оболочки, редакторы и интерпретаторы без осознанной причины.
- Учтены переменные окружения и `secure_path`.
- Итоговые права проверены через `sudo -l`.
- Есть журналирование и способ аварийного получения root-доступа.
## Связанные заметки
- [[Super-user]]
- [[File Permissions]]
- [[Linux - MOC]]
+73
View File
@@ -0,0 +1,73 @@
---
title: Defense-in-depth
status: seed
type: concept
tags:
- networking
- security
- infosec
- defense-in-depth
created: 2026-06-16
updated: 2026-06-16
aliases:
- DiD
- Глубокоэшелонированная защита
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
---
# Defense-in-depth
**Defense-in-depth** (**DiD**) — глубокоэшелонированная защита. Идея в том, чтобы строить несколько независимых слоёв контроля, которые замедляют злоумышленника и ограничивают ущерб при компрометации одного слоя.
Концепция заимствована из военной стратегии: защита не должна зависеть от одного барьера.
## Контуры защиты
DiD делит защиту на три группы.
**Физическая защита**:
- охрана и охранные системы;
- СКУД;
- видеонаблюдение;
- сигнализация;
- закрытые помещения, шкафы, замки, сейфы.
**Техническая защита**:
- контроль сетевого доступа;
- межсетевые экраны;
- антивирусная защита;
- прокси-серверы;
- системы аутентификации и авторизации.
**Административная защита**:
- политики доступа;
- регламенты обработки секретов;
- списки разрешённого и запрещённого ПО;
- правила работы с гостями, подрядчиками и внешними организациями.
## Практические направления
- ограничение привилегированного доступа;
- observability и аудит событий;
- культура DevSecOps;
- MFA;
- модели нулевого доверия;
- [[SASE]];
- централизованное управление секретами;
- безопасные практики разработки;
- регулярное сканирование инфраструктуры;
- сервисные сетки и [[Сегментация сети|сегментация]].
## Связь с ZTNA и SASE
[[Zero Trust Network Access|ZTNA]], [[SASE]] и DiD не конкурируют друг с другом. DiD задаёт общий принцип многоуровневой защиты, ZTNA описывает модель доступа, а SASE объединяет сетевой доступ и защитные сервисы в единую архитектуру.
## Связанные заметки
- [[Введение в сетевую безопасность]]
- [[Zero Trust Network Access]]
- [[SASE]]
- [[Сегментация сети]]
- [[Веб-аутентификация и авторизация]]
+61
View File
@@ -0,0 +1,61 @@
---
title: SASE
status: seed
type: concept
tags:
- networking
- security
- infosec
- sase
created: 2026-06-16
updated: 2026-06-16
aliases:
- Secure Access Service Edge
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
---
# SASE
**SASE** (**Secure Access Service Edge**) — подход на стыке сетевого подключения и информационной безопасности. Его цель — дать безопасный и оптимальный доступ к IT-ресурсам независимо от того, где находятся пользователь, устройство и приложение.
Концепция SASE была сформулирована Gartner в 2019 году. Это не один продукт, а архитектурная идея конвергенции сетевого доступа и защитных сервисов.
## Предпосылки
- размытие сетевого периметра;
- рост облачных сервисов;
- удалённая работа;
- использование личных устройств;
- необходимость сочетать безопасность и качество канала.
## Базовые компоненты
- **[[Zero Trust Network Access|ZTNA]]** — доступ по модели нулевого доверия;
- **SD-WAN** — программно-управляемый выбор канала;
- **SWG** — безопасный веб-шлюз;
- **CASB** — брокер безопасного доступа к облачным сервисам;
- **FWaaS** — межсетевой экран как сервис.
## Дополнительные компоненты
Вендоры могут включать в SASE дополнительные механизмы:
- DLP;
- QoS;
- NGFW;
- [[VPN]];
- UEBA;
- антифрод;
- защиту DNS и Wi-Fi;
- инструменты обфускации.
## Связь с другими подходами
SASE включает [[Zero Trust Network Access|ZTNA]] как один из базовых компонентов. В рамках [[Defense-in-depth]] SASE можно рассматривать как один из архитектурных слоёв защиты доступа и сетевого трафика.
## Связанные заметки
- [[Введение в сетевую безопасность]]
- [[Zero Trust Network Access]]
- [[Defense-in-depth]]
- [[VPN]]
- [[Сегментация сети]]
@@ -0,0 +1,64 @@
---
title: Zero Trust Network Access
status: seed
type: concept
tags:
- networking
- security
- infosec
- ztna
created: 2026-06-16
updated: 2026-06-16
aliases:
- ZTNA
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
---
# Zero Trust Network Access
**Zero Trust Network Access** (**ZTNA**) — модель доступа к ресурсам на основе принципа нулевого доверия. Решение о доступе принимается при каждом обращении к ресурсу, а не один раз после подключения к «доверенной» сети.
ZTNA не отменяет модель угроз, а меняет её: базовый сценарий предполагает, что сеть, устройство или пользователь уже могут быть скомпрометированы.
## Нулевое доверие
Концепция нулевого доверия исходит из того, что доверенных подключений нет. Доступ должен проверяться постоянно, с учётом контекста запроса, состояния пользователя, устройства и ресурса.
ZTNA часто выступает рыночной упаковкой Zero Trust, но сама концепция не привязана к конкретному вендору или технологии.
## Сценарии применения
- удалённый доступ с личных устройств;
- гостевой, подрядный и временный доступ;
- контроль IoT-устройств;
- доступ к внутренним ресурсам без прямого расширения периметра через [[VPN]].
## Контексты доступа
Для принятия решения ZTNA может учитывать:
- сетевой контекст: тип подключения, геолокация, источник запроса;
- контекст устройства: модель, ОС, обновления, состояние безопасности;
- пользовательский контекст: идентификация и аутентификация;
- ролевой контекст: авторизация и необходимые ресурсы;
- контекст безопасности: признаки компрометации, риск запроса, состояние учётной записи.
## Внедрение
Перед внедрением нужно определить сценарии применения, роли пользователей, ресурсы, контексты доступа и оценку рисков.
Практические первые шаги:
1. Включить MFA там, где это применимо.
2. Отказаться от статичного доверия к IP-адресам и allowlist.
3. Идентифицировать каждого пользователя и каждый запрос.
4. Сегментировать ресурсы и доступы к ним.
5. Постепенно убрать разделение пользователей на «своих» и «чужих».
## Связанные заметки
- [[Введение в сетевую безопасность]]
- [[SASE]]
- [[Defense-in-depth]]
- [[VPN]]
- [[Веб-аутентификация и авторизация]]
- [[Сегментация сети]]
@@ -1,20 +1,23 @@
--- ---
status: seed title: Введение в сетевую безопасность
status: processing
type: concept type: concept
tags: tags:
- networking - networking
- security - security
- infosec - infosec
- vulnerability-scanning
created: 2025-12-17 created: 2025-12-17
updated: 2026-05-07 updated: 2026-06-16
aliases: aliases:
- Введение в сетевую безопасность - Введение в сетевую безопасность
source: "[[Безопасность инфраструктуры. ZTNA, SASE, DiD]]"
--- ---
# Введение в сетевую безопасность # Введение в сетевую безопасность
**Сетевая безопасность** — часть информационной безопасности, которая защищает сетевую инфраструктуру, передаваемые данные и доступ к сетевым ресурсам. **Сетевая безопасность** — часть информационной безопасности, которая защищает сетевую инфраструктуру, передаваемые данные и доступ к сетевым ресурсам.
Сетевая безопасность связана с архитектурой сети, политиками доступа, сегментацией, мониторингом и защитой от атак. В контексте сети рядом лежат темы [[Сегментация сети]], [[VPN]] и [[Networking - MOC]]. Сетевая безопасность связана с архитектурой сети, политиками доступа, [[Сегментация сети|сегментацией]], мониторингом и защитой от атак. В контексте сети рядом лежат темы [[VPN]], [[Веб-аутентификация и авторизация]] и [[Networking - MOC]].
## CIA: базовая модель безопасности ## CIA: базовая модель безопасности
@@ -67,8 +70,42 @@ aliases:
3. Отпечаток сравнивается с базой известных уязвимостей. 3. Отпечаток сравнивается с базой известных уязвимостей.
4. Формируется отчёт с найденными проблемами. 4. Формируется отчёт с найденными проблемами.
Сканеры инфраструктуры помогают находить слабые шифры, небезопасные протоколы, дефолтные пароли, открытые порты, устаревшие приложения, просроченные сертификаты и ошибки конфигурации. Практический смысл отчёта — не сам список находок, а приоритизация рисков и митигация по рекомендациям инструмента.
OpenVAS, современная ветка которого связана с Greenbone, можно развернуть в контейнере для учебной проверки подконтрольной инфраструктуры. Типовой сценарий:
1. Запустить OpenVAS/Greenbone.
2. Создать target с адресом проверяемого узла.
3. Создать scan task.
4. Дождаться статуса `Done`.
5. Разобрать findings и поле `Solution`.
## Архитектурные подходы
Отдельные подходы к защите инфраструктуры:
- [[Zero Trust Network Access]] — модель доступа на основе нулевого доверия.
- [[SASE]] — конвергенция сетевого доступа и защитных сервисов.
- [[Defense-in-depth]] — многоуровневая защита, где компрометация одного слоя не должна ломать всю систему.
## Источники
- [Базовое описание ZTNA](https://codeby.net/threads/chto-takoye-ztna-i-zachem-on-nuzhen.84541/)
- [Zero Trust архитектура в 2025 году](https://securitymedia.org/info/zero-trust-arkhitektura-v-2025-godu-printsipy-kontseptsii-nulevogo-doveriya-i-ee-razvitie.html)
- [Практические рекомендации по переходу к Zero Trust](https://www.kaspersky.ru/blog/zero-trust-transition-practical-advice/39484/)
- [SASE и ИИ](https://habr.com/ru/articles/914496/)
- [Defense-in-depth на примере XZ Utils](https://www.wiz.io/academy/defense-in-depth/)
- [Defense-in-depth: уровни контроля](https://www.wallarm.com/what/defense-in-depth-concept/)
- [Defense-in-depth](https://www.fortinet.com/resources/cyberglossary/defense-in-depth/)
- [Modern defense-in-depth strategies](https://www.isaca.org/resources/news-and-trends/isaca-now-blog/2025/beyond-the-moat-modern-defense-in-depth-strategies/)
## Связанные заметки ## Связанные заметки
- [[Networking - MOC]] - [[Networking - MOC]]
- [[Сегментация сети]] - [[Сегментация сети]]
- [[VPN]] - [[VPN]]
- [[Веб-аутентификация и авторизация]]
- [[CA и NGINX]]
- [[Zero Trust Network Access]]
- [[SASE]]
- [[Defense-in-depth]]
View File
View File
@@ -0,0 +1,181 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-16
aliases: []
---
# CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов
## Часть 2. Пайплайны в GitlabCI
Пайплайны в GitlabCI строятся на основе языков **Shell** и **Yaml**. С помощью них можно описывать этапы процессов CI/CD и их желаемое поведение.
Язык `yaml` позволяет описывать конфигурацию этапов в декларативном ключе. Основной файл, в котором описывается работа пайплайна, носит название `Gitlab-ci.yml`.
### Shell
В GitlabCI при описании этапов пайплайна можно задействовать `shell`.
> **Shell** — командный интерпретатор операционных систем семейства Unix.
Shell позволяет существенно расширить возможности описания процессов CI/CD.
### Stage
Структура файла `Gitlab-ci.yml` предполагает разбиение пайплайна на этапы (`Stages`).
Каждый Stage представляет из себя этап пайплайна с полным описанием его работы.
Этапы (Stages) могут выполняться **последовательно** или **параллельно**.
Так могут выглядеть типовые этапы (Stages) в пайплайне GitlabCI.
```yaml
stages:
- build
- test
- deploy
build-job:
stage: build
script:
- do some build
test-job:
stage: test
script:
- do some test
deploy-job:
stage: deploy
script:
- do some deploy
```
**Запуск пайплайна можно инициировать тремя способами:**
- *Вручную* (через веб-интерфейс или API).
- *Автоматически* по событию (Push, Commit, Merge-request, etc) - как триггеры в PSQL
- Используя *Web-hook*.
**Как ручной, так и автоматический запуск пайплайнов может быть дополнительно параметризован при запуске:**
- Указание специфических переменных.
- Выбор учетных данных (Credentials).
- Сборка по конкретному коммиту, ветке, тэгу.
- .. и многое другое
**Вложенные пайплайны:**
В зависимости от сложности процессов CI/CD, пайплайны могут быть составными — содержать вложенные пайплайны, выполняющиеся последовательно или параллельно.
**Интеграция:**
В случае использования в процессах CI/CD нескольких инструментов, возможна их интеграция (к примеру, исходный код хранится в gitlab, а пайплайны запускаются в Jenkins) через встроенный в GitlabCI механизм интеграций.
**GitlabCI имеет понятный веб интерфейс, в котором отображаются** **результаты прохождения пайплайнов и их отдельных этапов** **в простом и доступном виде:**
![](https://legacy.merionet.ru/images/devops/images/3758/img_01.png)
## Часть 3. Пайплайны в Jenkins
Для полноценной работы Jenkins будет достаточно предустановленных плагинов, но наличие минимально необходимых дополнительных плагинов существенно **облегчит** построение и модификацию пайплайнов.
![](https://legacy.merionet.ru/images/devops/images/3758/img_02.png)
Не забывайте своевременно производить обновление используемых плагинов, чтобы избежать ошибок и уязвимостей.
![](https://legacy.merionet.ru/images/devops/images/3758/img_07.png)
После установки плагина, его требуется настроить для работы, сделать это можно в настройках Jenkins:
- Глобальные настройки.
- Настройки инструментов.
- Настройки безопасности.
- Конфигурирование сред исполнения.
![](https://legacy.merionet.ru/images/devops/images/3758/img_06.png)
Настройки проекта в Jenkins сводятся к конфигурации самого проекта и его интеграций, а также настройке пайплайна.
Для описания поведения пайплайна мы можем использовать графический интерфейс или язык **Groovy**.
![](https://legacy.merionet.ru/images/devops/images/3758/img_04.png)
Если вы испытываете трудности с языком Groovy — вам придет на помощь встроенный редактор запросов.
Сгенерированный запрос можно использовать в описании вашего пайплайна.
![](https://legacy.merionet.ru/images/devops/images/3758/img_05.png)
Хорошей практикой является описание пайплайна в отдельном файле, который хранится не в Jenkins и подвергается защите от несанкционированного внесения изменений.
Такой файл часто носит название Jenkinsfile и хранится в репозитории (например, в Gitlab), а не в самом Jenkins.
![](https://legacy.merionet.ru/images/devops/images/3758/img_03.png)
Хранящийся в репозитории Jenkinsfile, вместе с самим исходным кодом продукта, защищен от изменений (их могут вносить только уполномоченные члены команды или с использованием процедуры Merge-request).
![](https://legacy.merionet.ru/images/devops/images/3758/img_08.png)
Инициировать процедуру запуска пайплайна можно несколькими способами:
- Вручную (веб-интерфейс или API).
- Автоматически, с использованием интеграций (посредством плагинов).
- Используя Web-hook.
- С помощью планировщика сборок.
![](https://legacy.merionet.ru/images/devops/images/3758/img_09.png)
Имея опыт работы с языком **Groovy**, используя разнообразные плагины и следуя хорошим практикам организации пайплайнов, можно добиться грамотного построения процессов CI/CD:
- Параметризованные сборки.
- Хранение Jenkinsfile в репозитории с ограничением на изменение.
- Интеграция с Git, Kubernetes, Инструментами обеспечения информационной безопасности и Observability.
- Отказоустойчивая и безопасная конфигурация доступа к Jenkins для других инструментов.
- Визуализация процессов.
## Часть 4. Внедрение и развитие процессов CI/CD
## Внедрение и развитие процессов CI/CD
В зависимости от того, в каком состоянии находятся существующие процессы разработки, тестирования и администрирования, процессы CI/CD должны внедряться предсказуемо и поэтапно.
Любые существующие процессы CI/CD должны развиваться не прекращаясь.
**Обогатить имеющиеся процессы непрерывной сборки и доставки** **можно следующими практиками:**
- IaC.
- Orchestration.
- Observability.
- Info-security.
При настройке процессов непрерывной доставки используйте подход декларативного описания инфраструктуры и конфигурации в виде кода (практики IaC).
Старайтесь придерживаться микросервисной архитектуры там, где это оправдано.
Ориентируйтесь на контейнерные технологии.
Изучайте и применяйте технологии оркестрации контейнерных и неконтейнерных рабочих нагрузок в режиме кластера.
**Не пренебрегайте развитием практик обеспечения Observability:**
- Мониторинг.
- Трассировки.
- Аудит.
- Журналирование.
- Обработка логов.
- Сбор обратной связи.
Дополняйте этими практиками этапы своих пайплайнов, или добавляйте новые этапы на их основе.
**Существующие практики CI/CD можно и нужно обогащать** **внедрением проверок по информационной безопасности из** **методологии Devsecops:**
- Различные виды анализа (SAST, SCA, DAST, IAST, RASP).
- Сканирование инфраструктуры.
- Использование профильных инструментов для работы с сенсетивной информацией.
Помните о том, что процессы CI/CD — это лишь одна из множества Devops-практик, которые важно и нужно применять для создания благоприятной атмосферы в коллективной работе над цифровым продуктом.
**Devops** — это, в первую очередь, культура взаимодействия всех участников жизненного цикла цифрового продукта.
@@ -0,0 +1,201 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-10
aliases: []
---
# CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins
## Процессы CI/CD
**CI/CD (Continuous integration and Continuous delivery)** — широко распространенная devops-практика, подразумевающая непрерывную сборку и доставку цифрового продукта до конечной инфраструктуры.
### Continuous integration
Методология, при которой изменения в исходный код вносятся последовательно и непрерывно.
Исходный код подвергается проверкам, тестируется, упаковывается в артефакт, помещается в хранилище и готовится к отправке на развертывание.
### Continuous delivery
Практика, являющаяся логическим продолжением непрерывной сборки (интеграции) цифрового продукта.
Артефакт, прошедший все этапы непрерывной сборки, немедленно развертывается на конечной инфраструктуре, где в виде цифрового продуктаьвыполняет свои функции.
**Цели CI/CD:**
- Обеспечить последовательный и непрерывный процесс сборки, проверки, тестирования и развертывания цифровых продуктов.
- Автоматизировать процедуру сборки и доставки.
- Минимизировать возможные ошибки и облегчить процесс их исправления.
- Поддержать современный взгляд на гибкие методологии разработки.
**Преимущества CI/CD:**
- Максимальная автоматизация процессов.
- Своевременное обнаружение ошибок.
- Сокращение цикла получения обратной связи.
- Разделение сред разработки, тестирования и эксплуатации цифрового продукта.
- Глубокая наблюдаемость (Observability).
**Распространенные этапы CI/CD:**
- Создание кода / интеграция функционала.
- Модульное тестирование.
- Сборка / компилирование.
- Доставка артефакта и его развертывание.
- Дополнительные проверки.
- Сбор метрик / сбор обратной связи.
**Начальный этап:**
Этап создания кода (интеграции нового функционала) подразумевает наличие системы контроля версий, где осуществляется коллективная работа над исходным кодом цифрового продукта и ведется разработка функционала (исправление ошибок).
В процессах CI/CD это начальный этап сборки и доставки.
**Автоматизация:**
Модульное тестирование относится к логическим проверкам исходного кода, не подразумевающее запуск приложения на исполнение.
В первую очередь необходимо самим разработчикам для уверенности в отсутствии логических ошибок в разрабатываемых участках исходного кода.
Процессы CI/CD позволяют автоматизировать запуск таких проверок.
**Сборка:**
Сборка (компилирование) продукта из исходного кода чаще всего осуществляется с применением стандартизированной упаковки приложения, готового к запуску на конечной инфраструктуре.
Сборка часто является заключительным этапом процесса CI, далее собранный и готовый к запуску *артефакт* передается процессу непрерывной доставки (но могут иметь место и дополнительные проверки).
**Доставка:**
Доставка собранного и проверенного артефакта до конечной инфраструктуры осуществляется в процессе CD.
Доставленный и развернутый артефакт является конечным продуктом, функции которого эксплуатируют потребители.
**Проверка:**
В зависимости от среды, до которой был доставлен артефакт, он может быть подвергнут дополнительным проверкам, прежде чем отправиться в эксплуатацию.
Различают несколько различных видов сред цифрового продукта:
- Среда разработки (Dev).
- Среда тестирования (Test, QA, Stage, Integration, Pre-production, Uat,..).
- Среда эксплуатации (Prod).
**После прохождения всех этапов процессы CI/CD не заканчиваются:**
- Важно собирать обратную связь о процессе эксплуатации продукта (согласно потребительским метрикам).
- Собирать статистику (Метрики, Логи, Журналы, Трассировки).
- Процессы CI/CD не предполагают остановки и возобновляются с каждым новым коммитом и изменением в исходном коде.
### Пайплайны
Все этапы, составляющие процессы CI/CD, называются пайплайнами (Pipelines).
Создание, изменение, поддержка и совершенствование пайплайнов возможны в специализированных инструментах — **сборочных конвейерах CI/CD.**
## Часть 3. Знакомство с GitlabCI
**GitlabCI (Gitlab CI/CD)** — opensource-инструмент, встроенный в систему управления версиями Gitlab (изначально выпускался как отдельный проект).
GitlabCI выполняет функции сборочного конвейера, позволяющего осуществлять все этапы CI/CD.
**Преимущества GitlabCI:**
- Совместимость с Gitlab.
- Простота интеграции в рабочие процессы.
- Развитое комьюнити.
- Автомасштабирование.
GitlabCI относительно новый инструмент, быстро завоевавший популярность в сообществе.
Своей известностью GitlabCI обязан в первую очередь нативностью с Gitlab, достаточной функциональностью и простотой в использовании.
GitlabCI полностью совместим с системой управления версиями Gitlab, изначально предполагая интеграцию Gitlab в процесс непрерывной сборки (CI).
Однако, возможна интеграция и с другими системами управления версиями.
**GitlabCI обладает множеством полезных возможностей** **для улучшения процесса автоматизации сборки и доставки,** **интеграция которых в рабочий процесс не составит труда:**
- Отслеживание проектов и групп.
- Тонкая настройка пайплайнов.
- Мониторинг состояния задач в этапах CI/CD.
- Анализ результатов выполнения этапов.
GitlabCI обладает встроенным механизмом по автоматическому масштабированию сред исполнения рабочих задач (Runners).
Выполнение задач по сборке может осуществляться на Runners под управлением ВМ на разных ОС, Docker, Kubernetes.
## Часть 4. Знакомство с Jenkins
**Jenkins** — Open-source инструмент CI/CD, предназначенный для автоматизации множества задач в цифровых проектах и являющийся сборочным конвейером.
**Достоинства и преимущества Jenkins:**
- Простота в установке, настройке и эксплуатации.
- Расширяемость за счет использования Jenkins плагинов.
- Развитое сообщество и богатая документация.
Jenkins легко устанавливать, настраивать и обновлять благодаря простым процедурам установки без лишних деталей и специфичных особенностей.
Jenkins обладает обширной и понятной документацией.
В Jenkins создана целая экосистема различных плагинов, расширяющая или модифицирующая существующий функционал, в том числе графический интерфейс.
Плагинов насчитывается более 1500, практически все из них бесплатны и очень просты в установке.
Jenkins обладает самым развитым сообществом по сравнению с другими сборочными конвейерами CI/CD.
К комьюнити относятся не только команда разработки самого инструмента, но и разработчики множества плагинов.
## Часть 5. GitlabCI vs Jenkins
**Сходства GitlabCI и Jenkins:**
- Открытый исходный код.
- Кроссплатформенность.
- Простота в установке и настройке.
- Гибкая работа с пайплайнами.
- Поддержка API.
- Интегрируемость с другими системами в рамках рабочих процессов.
- Обширное неравнодушное комьюнити.
### Возможности
**Уникальные возможности GitlabCI:**
- Нативность Gitlab.
- Не требуется отдельная установка.
- Собственный мониторинг производительности.
- Встроенные решения по проверке качества кода.
**Уникальные возможности Jenkins:**
- Развитая экосистема плагинов.
- Расширенные возможности по интеграции со множеством систем за счет использования плагинов.
- Возможность использования языка Groovy для описания пайплайнов.
- Более обширное комьюнити, лучше документация.
### Недостатки
**Недостатки GitlabCI:**
- Специфичная работа с артефактами.
- Возможные трудности при описании и работе со сложными пайплайнами.
- Местами чувствуется недостаточность языков описания пайплайнов.
**Недостатки Jenkins:**
- Нагромождение плагинов может вызывать сложности в их поддержке и обновлении.
- Для небольших проектов и небольших команд работа может оказаться сложнее, чем с GitlabCI.
GitlabCI и Jenkins замечательные инструменты, обладающие впечатляющими возможностями.
Если вы используете Gitlab в своей работе, логичнее использовать GitlabCI для процессов CI/CD.
Если вы не привязаны к Gitlab и вас впечатляет экосистема плагинов, выбирайте Jenkins.
Оба инструмента прекрасно справляются со своими задачами.
@@ -0,0 +1,89 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-15
aliases: []
---
# Proxmox VE — local, local-lvm, LVM
## Что такое local и local-lvm
|Storage|Тип|Путь / устройство|Хранит|
|---|---|---|---|
|`local`|directory|`/var/lib/vz`|ISO, CT-шаблоны, бэкапы, snippets|
|`local-lvm`|LVM thin pool|`pve/data` (LV в VG `pve`)|Диски VM/CT, снапшоты дисков|
## Куда скачиваются ISO через веб-интерфейс
По умолчанию — в `local` (`/var/lib/vz/template/iso/`).
Чтобы скачивать на другой storage — выбрать его в выпадающем списке при Download from URL.
Условие: у storage должен быть включён Content type **ISO image** (Datacenter → Storage → Edit → Content).
## Проверить содержимое LVM
```bash
# Список всех LV с размерами
lvs
# Storage Proxmox с backing-устройствами
pvesm status
```
Thin pool определяется по атрибуту `twi` в колонке `Attr`.
## Удалить local-lvm и расширить root
> Перед удалением убедиться, что на `local-lvm` нет дисков VM/CT (Data% = 0.00).
### 1. Удалить thin pool
```bash
sudo lvremove /dev/pve/data
# подтвердить: y
```
### 2. Расширить root на нужный размер
```bash
# Добавить конкретный размер (например, 60G):
sudo lvresize -L +60G /dev/pve/root
# Или отдать всё свободное место:
sudo lvresize -l +100%FREE /dev/pve/root
```
### 3. Расширить файловую систему
```bash
sudo resize2fs /dev/pve/root
```
Работает online, перезагрузка не нужна.
### 4. Удалить storage из GUI
Datacenter → Storage → `local-lvm` → Remove
### 5. Проверить результат
```bash
lsblk
lvs
df -h /
```
## Что делать с оставшимся свободным местом в VG pve
Нераспределённое пространство можно использовать:
- добавить как новый LVM storage в Proxmox
- создать новый LV под конкретную задачу
- оставить в резерве
```bash
# Посмотреть свободное место в VG:
vgs
```
+402
View File
@@ -0,0 +1,402 @@
---
title: Sudoers
status: seed
type: guide
tags:
- linux
- sudo
- security
created: 2026-06-15
updated: 2026-06-15
aliases:
- sudoers
- Настройка sudo
---
# Sudoers
`sudo` запускает команду с полномочиями другого пользователя, обычно `root`. Политика доступа задаётся в `/etc/sudoers` и дополнительных файлах из `/etc/sudoers.d/`.
`sudo` не является заменой файловых прав. Он выдаёт полномочия на конкретное действие после проверки пользователя, хоста, целевого пользователя, команды и параметров политики.
## Где хранится конфигурация
- `/etc/sudoers` — основной файл.
- `/etc/sudoers.d/` — каталог для отдельных правил.
- `/var/log/` или журнал systemd — записи о вызовах `sudo`, в зависимости от конфигурации системы.
Основной файл обычно содержит:
```sudoers
@includedir /etc/sudoers.d
```
Для локальных правил удобнее создавать отдельный файл:
```bash
sudo visudo -f /etc/sudoers.d/backup
```
Имена файлов в `sudoers.d` лучше делать без точки и символа `~`: некоторые реализации игнорируют такие файлы. Права должны исключать запись обычными пользователями:
```bash
sudo chown root:root /etc/sudoers.d/backup
sudo chmod 0440 /etc/sudoers.d/backup
```
## Редактирование через visudo
Не следует редактировать `/etc/sudoers` обычным редактором. `visudo` блокирует файл от одновременного изменения и проверяет синтаксис перед сохранением.
```bash
sudo visudo
sudo visudo -f /etc/sudoers.d/backup
sudo visudo -c
```
Если ошибка уже внесена, исправление потребует действующей root-сессии, загрузки в recovery mode или другого способа получить root-доступ.
Редактор можно выбрать переменными окружения:
```bash
sudo EDITOR=nvim visudo
```
Возможность выбора редактора зависит от настроек `sudo`; небезопасные переменные окружения могут быть отброшены.
## Формат правила
Базовая форма:
```sudoers
пользователь хост=(целевой_пользователь:целевая_группа) теги: команды
```
Пример:
```sudoers
ada ALL=(root) /usr/bin/systemctl restart nginx
```
Значение полей:
| Поле | Смысл |
|---|---|
| `ada` | кому разрешено использовать правило |
| `ALL` | на каких хостах действует правило |
| `(root)` | от имени какого пользователя разрешён запуск |
| `/usr/bin/systemctl restart nginx` | разрешённая команда и аргументы |
Группа указывается с `%`:
```sudoers
%wheel ALL=(ALL:ALL) ALL
```
Это разрешает членам группы `wheel` запускать любые команды от имени любого пользователя и группы. Такое правило выдаёт полный административный доступ.
## Команды и аргументы
В правилах следует использовать абсолютный путь:
```sudoers
ada ALL=(root) /usr/bin/systemctl status nginx
```
Путь можно узнать командой:
```bash
command -v systemctl
```
Аргументы являются частью разрешения. Эти правила различаются:
```sudoers
ada ALL=(root) /usr/bin/systemctl restart nginx
ada ALL=(root) /usr/bin/systemctl restart *
```
Первое разрешает перезапустить только `nginx`. Второе намного шире и может разрешить перезапуск произвольного юнита.
Правило без аргументов в современных версиях `sudo` может разрешать команду с любыми аргументами. Для явного запрета аргументов используется пустая строка:
```sudoers
ada ALL=(root) /usr/bin/id ""
```
Поведение сопоставления аргументов и специальных символов зависит от версии `sudo`. После изменения правило нужно проверять на целевой системе.
## Псевдонимы
Псевдонимы уменьшают повторение в больших конфигурациях.
### User_Alias
```sudoers
User_Alias OPERATORS = ada, alice, %ops
```
### Runas_Alias
```sudoers
Runas_Alias SERVICE_USERS = nginx, postgres
```
### Host_Alias
```sudoers
Host_Alias WEB_SERVERS = web01, web02
```
### Cmnd_Alias
```sudoers
Cmnd_Alias NGINX_CONTROL = \
/usr/bin/systemctl status nginx, \
/usr/bin/systemctl reload nginx, \
/usr/bin/systemctl restart nginx
%ops WEB_SERVERS=(root) NGINX_CONTROL
```
Имена псевдонимов принято писать в верхнем регистре.
## Теги
### PASSWD и NOPASSWD
По умолчанию `sudo` запрашивает пароль вызывающего пользователя:
```sudoers
ada ALL=(root) /usr/bin/systemctl restart nginx
```
Без пароля:
```sudoers
ada ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
```
`NOPASSWD` удобно для автоматизации, но увеличивает последствия компрометации учётной записи. Его следует ограничивать минимальным набором команд.
Теги действуют на следующие команды в списке, пока не будут переопределены:
```sudoers
ada ALL=(root) NOPASSWD: /usr/bin/systemctl status nginx, \
PASSWD: /usr/bin/systemctl restart nginx
```
### NOEXEC
```sudoers
ada ALL=(root) NOEXEC: /usr/bin/less /var/log/nginx/error.log
```
`NOEXEC` пытается запретить программе запускать другие процессы. Это дополнительная мера, а не надёжная граница безопасности: поддержка зависит от платформы и типа бинарника.
### SETENV
```sudoers
ada ALL=(root) SETENV: /usr/local/sbin/deploy
```
`SETENV` позволяет передавать переменные окружения с меньшим числом ограничений. Это опасно для команд, поведение которых зависит от `PATH`, загрузчиков библиотек, интерпретаторов и конфигурационных переменных.
## Defaults
Директивы `Defaults` управляют поведением `sudo`.
```sudoers
Defaults env_reset
Defaults use_pty
Defaults timestamp_timeout=5
Defaults passwd_tries=3
```
Часто используемые параметры:
| Параметр | Назначение |
|---|---|
| `env_reset` | оставляет ограниченный набор переменных окружения |
| `secure_path` | задаёт доверенный `PATH` для команд через `sudo` |
| `use_pty` | запускает команду в псевдотерминале |
| `timestamp_timeout` | срок действия успешной аутентификации в минутах |
| `passwd_tries` | число попыток ввода пароля |
| `log_input`, `log_output` | запись ввода и вывода поддерживаемых сессий |
Пример `secure_path`:
```sudoers
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/bin"
```
Настройки можно ограничить пользователем, группой, хостом или командой:
```sudoers
Defaults:ada timestamp_timeout=0
Defaults:%ops use_pty
Defaults!/usr/bin/systemctl !log_output
```
`timestamp_timeout=0` требует пароль для каждого отдельного запуска. Отрицательное значение создаёт сессию без ограничения по времени и обычно нежелательно.
## Запуск от имени другого пользователя
```sudoers
ada ALL=(postgres) /usr/bin/psql
```
Использование:
```bash
sudo -u postgres /usr/bin/psql
```
С указанием группы:
```sudoers
ada ALL=(deploy:deploy) /usr/local/bin/release
```
## Запреты
Команду можно исключить через `!`:
```sudoers
ada ALL=(root) ALL, !/usr/bin/su, !/usr/bin/bash
```
Такой список не создаёт безопасное ограничение полного административного доступа. Пользователь с разрешением `ALL` часто может получить оболочку другим способом: через редактор, интерпретатор, отладчик, файловый менеджер, загрузку модуля, изменение исполняемого файла или сервисной конфигурации.
Отрицания полезны для уточнения узкого списка, но не должны использоваться как sandbox.
## Опасные категории команд
Нельзя считать ограниченными команды, которые позволяют:
- запустить оболочку или произвольный процесс;
- выполнять код или загружать модули;
- редактировать произвольные файлы;
- менять владельца, права или ACL;
- записывать в исполняемые файлы и каталоги из `PATH`;
- менять unit-файлы, cron-задачи, PAM, загрузчик или конфигурацию сервисов;
- читать секреты и закрытые ключи;
- управлять контейнерами или виртуальными машинами с доступом к хосту.
Примеры команд, которые часто позволяют выйти к root:
```text
sh, bash, zsh, python, ruby, perl, vim, nvim, less, find,
tar, rsync, awk, sed, env, systemctl, docker, podman
```
Риск зависит от разрешённых аргументов, файловой системы и окружения. Само имя команды недостаточно для оценки безопасности.
## Безопасная автоматизация
Предпочтительный подход — разрешить один root-owned wrapper с фиксированным поведением:
```sudoers
deploy ALL=(root) NOPASSWD: /usr/local/sbin/restart-myapp
```
Требования к wrapper:
- файл и родительские каталоги не доступны на запись вызывающему пользователю;
- используются абсолютные пути;
- входные параметры проверяются по allowlist;
- не выполняются строки через shell без необходимости;
- окружение очищается или задаётся явно;
- временные файлы создаются безопасно;
- логируются значимые действия.
Если пользователь может изменить разрешённый скрипт, библиотеку, конфигурацию или исполняемый файл, правило фактически разрешает выполнение произвольного кода с повышенными правами.
## Проверка правил
Показать доступные текущему пользователю команды:
```bash
sudo -l
```
Проверить другого пользователя от root:
```bash
sudo -l -U ada
```
Сбросить сохранённую аутентификацию:
```bash
sudo -k
```
Удалить timestamp полностью:
```bash
sudo -K
```
Проверить конфигурацию:
```bash
sudo visudo -c
```
Проверять нужно не только синтаксис, но и фактический результат `sudo -l`: правила могут объединяться, а более широкое разрешение в другом файле отменяет ожидаемое ограничение.
## Приоритет и объединение правил
`sudoers` не работает как обычный firewall с простым правилом «первое совпадение победило». Для пользователя могут совпасть несколько записей; разрешения и теги вычисляются по правилам sudoers и могут объединяться.
Практические следствия:
- узкое правило не отменяет широкое разрешение из другого файла;
- порядок важен для некоторых тегов и параметров;
- членство пользователя в нескольких административных группах нужно учитывать;
- проверять итог следует через `visudo -c` и `sudo -l`.
## Минимальный пример
Задача: разрешить группе `webops` проверять и перезапускать только `nginx`.
```sudoers
Cmnd_Alias NGINX_READ = \
/usr/bin/systemctl status nginx
Cmnd_Alias NGINX_WRITE = \
/usr/bin/systemctl reload nginx, \
/usr/bin/systemctl restart nginx
%webops ALL=(root) NGINX_READ
%webops ALL=(root) PASSWD: NGINX_WRITE
```
Проверка:
```bash
sudo visudo -c
sudo -l
sudo /usr/bin/systemctl status nginx
```
## Чек-лист
- Правило хранится в отдельном файле `/etc/sudoers.d/`.
- Файл проверен через `visudo`.
- Использованы абсолютные пути.
- Разрешены конкретные команды и аргументы.
- Нет лишнего `ALL`.
- `NOPASSWD` используется только при необходимости.
- Пользователь не может изменить разрешённую команду или её зависимости.
- Не разрешены оболочки, редакторы и интерпретаторы без осознанной причины.
- Учтены переменные окружения и `secure_path`.
- Итоговые права проверены через `sudo -l`.
- Есть журналирование и способ аварийного получения root-доступа.
## Связанные заметки
- [[Super-user]]
- [[File Permissions]]
- [[Linux - MOC]]
@@ -1,3 +1,7 @@
---
created: 2026-06-08
updated: 2026-06-09
---
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ
Федеральное государственное автономное образовательное учреждение высшего образования Федеральное государственное автономное образовательное учреждение высшего образования
@@ -143,3 +147,4 @@ Server-rendered HTML через Hotwire (Turbo + Stimulus) снимает нео
3. Salunke S.V., Ouda A. A Performance Benchmark for the PostgreSQL and MySQL Databases // Future Internet (MDPI). 2024. Vol. 16(10). Art. 382. DOI: 10.3390/fi16100382. 3. Salunke S.V., Ouda A. A Performance Benchmark for the PostgreSQL and MySQL Databases // Future Internet (MDPI). 2024. Vol. 16(10). Art. 382. DOI: 10.3390/fi16100382.
4. Даньшин К.А. Сравнительный анализ современных веб-фреймворков для разработки приложений // CyberLeninka, 2019. 4. Даньшин К.А. Сравнительный анализ современных веб-фреймворков для разработки приложений // CyberLeninka, 2019.
5. Chris Richardson. Microservices Patterns: With examples in Java. Manning Publications. (Pattern: Transactional outbox). 5. Chris Richardson. Microservices Patterns: With examples in Java. Manning Publications. (Pattern: Transactional outbox).
@@ -0,0 +1,296 @@
---
status: seed
type: concept
tags: []
created: 2025-12-17
updated: 2026-06-16
aliases: []
---
# Безопасность инфраструктуры. ZTNA, SASE, DiD
## Zero trust network access
**ZTNA** - Zero Trust Network Access - давно известный подход по обеспечению безопасности, основанный на концепции нулевого доверия.
ZTNA является реинкарнацией старого подхода ZT, своеобразной рыночной упаковкой.
> [!note] Бесспорные факторы популярности подхода ZTNA:
> - Возрастающая популярность облачных технологий.
> - Массовый переход на удаленный формат работы (COVID-19).
### Нулевое доверие
Концепция **“нулевого доверия”** предполагает, что решение о предоставлении или непредоставлении доступа к ресурсу принимается каждый раз в момент самого обращения.
При сценарии “нулевого доверия” нет никаких доверенных подключений, поэтому моделирование угроз развивается изначально по худшему из возможных сценариев.
<u>*ZTNA* не отменяет существующую модель угроз, а изменяет ее</u>.
*Распространенные сценарии применения:*
1) Удаленный доступ с личных устройств
2) Гостевые и временные доступы
3) Контроль интернета вещей (IoT)
*Источники информации (контексты) для принятия решения о предоставлении или непредоставлении доступа в ZTNA:*
- Сетевой контекст (тип подключения, геолокация).
- Контекст устройства (модель, ОС, обновления, локализация).
- Пользовательский контекст (идентификация).
- Ролевой контекст (авторизация).
- Контекст безопасности (компрометация данных).
- .. и многое другие
*Внедрение ZTNA требует достаточных предварительных действий:*
1. Определить сценарии применения, контексты, оценку рисков.
2. Определить **роли** пользователей и необходимые им **ресурсы**.
3. Подобрать правильные, подходящие в вашем случае **инструменты**.
4. Поддержка вашего рабочего окружения.
5. Готовность внедрять ZTNA последовательно и поэтапно.
*Процесс внедрения ZTNA:*
1) Отказ от статических IP-адресов
2) Отказ от White-Lists
3) Идентифицировать каждого пользователя и каждый пользовательский запрос
4) Сегментировать имеющиеся ресурсы и доступы к ним
5) Отмена разделения пользователей на своих/чужих (гостей, подрядчиков, временных пользователей)
6) Внедрить многофакторную аутентификацию везде, где это применимо, но поэтапно.
**ZTNA** — это маркетинговая реинкарнация давно существующей модели "нулевого доверия".
Концептуально ZTNA не привязана к конкретной технологии или вендору.
Начать внедрять ZTNA можно и нужно поэтапно, начать достаточно с малого. К примеру, двух-факторная аутентификация пользователей поможет защититься более, чем от 70% потенциальных взломов*.
\**по версии журнала Anti-malware.*
## Secure access service edge
**SASE** — Secure Access Service Edge.
Достаточно новый подход, находящийся на стыке решения двух важных задач — обеспечения информационной безопасности и выбора оптимального способа подключения к ресурсам.
*SASE* является комплексным подходом и предоставляется множеством вендоров.
Концепция *SASE* разработана исследовательской компанией Gartner в 2019 году.
Главная цель *SASE* сформулирована следующим образом — сервис безопасного и оптимального доступа к IT-ресурсам (симбиоз выбора стабильного канала связи и обеспечения информационной безопасности ресурсов).
*Предпосылки возникновения SASE во многом схожи с ZTNA:*
- Размытие периметра инфраструктуры.
- Развитие облачных технологий.
- Массовый переход на удаленную работу.
- Использование личных устройств.
- COVID-19.
*Gartner составило список обязательных технологий, которые должны быть реализованы в SASE:*
- **ZTNA (Zero Trust Network Access)** - модель сетевого доступа к ресурсам на основе политики “нулевого доверия”.
- **SD-WAN (Software-Defined Wide Area Network)** — динамический подбор наилучшего способа подключения в зависимости от производительности.
- **SWG (Secure Web Gateway)** — безопасный сетевой шлюз.
- **CASB (Cloud Access Security Broker)** — брокер безопасного доступа к облачным ресурсам.
- **FWaaS (Firewall-as-a-Service)** — сервис облачного сетевого экрана.
*Помимо обязательных компонентов, вендоры SASE могут реализовывать дополнительные, необязательные компоненты:*
- **DLP** (data leak prevention) — предотвращение утечек.
- **QoS** (quality of service) — приоритизация трафика.
- **NGFW** (web application firewall).
- **VPN** (virtual private network) — виртуальная частная сеть.
- **UEBA** (user entity behaviour analysis) — поведенческий анализ.
- **Антифрод инструменты** — оценка вероятности финансового мошенничества.
- **Инструменты обфускации** — изменение исходного кода в целях затруднения анализа при сохранении функциональности.
- **Инструменты защиты DNS / WiFi от подмена и взлома.**
- и многие другие
Вендоры SASE-решений, реализовавшие основные компоненты, могут получить *сертификацию Gartner*.
Отечественных вендоров SASE-решений не существует до сих пор. (
*Прогноз популярности SASE по версии аналитиков Gartner:*
- **SASE** выйдет на плато *продуктивности* в течение 2024-2029.
- К 2024 году как минимум 40% предприятий будут иметь четкие стратегии по внедрению SASE (по сравнению с 1% на начало 2019 года).
- Вендоры будут переоценивать свои возможности по предоставлению услуг SASE.
Концепция SASE сформулирована компанией Gartner, она состоит из конвергентного решения по оптимизации доступа и информационной безопасности ИТ-ресурсов.
Реализовав список обязательных компонентов, вендор может претендовать на сертификацию своего решения у *Gartner*, реализовав как можно больше дополнительных компонентов эффективно конкурировать с другими вендорами.
## Defense-in-depth
**DiD** — Defense-in-depth.
DiD является концепцией глубокой защиты, в его основу заложены несколько степеней защиты, методы которых не пересекаются.
Подход призван задержать и усложнить продвижение и действия злоумышленника.
Концепция глубокоэшелонированной защиты заимствована из общей военной стратегии и разработана агентством национальной безопасности США.
*DiD делит организацию защиты инфраструктуры на три контролируемые части:*
- Физическая средства защиты.
- Технические средства защиты.
- Административные средства защиты.
*К физическим средствам защиты можно отнести:*
- Охранники и охранные системы.
- Системы контроля и управления доступом (СКУД)
- Видеонаблюдение (CCTV)
- Сигнализационные системы.
- Запертые двери, шкафы, замки, сейфы.
*К техническим средствам защиты можно отнести:*
- Контроль сетевого доступа.
- Межсетевые экраны.
- Антивирусная защита.
- Прокси-серверы.
- Системы аутентификации и авторизации.
*К административным средствам защиты можно отнести:*
- Регулирование и управления самих средств защиты (документы и нормативка?)
- Обработка сенсетивной информации (пароли, токены и т.д.)
- Ведение списков разрешенных и запрещенных ПО.
- Политики взаимодействия с гостевыми допусками, внешними ресурсами и организациями.
*С чего начать внедрение DiD:*
- Включение защиты от привилегированного доступа.
- Использование средств *Observability*.
- Развитие культуры DevSecOps.
- Внедрение многофакторной аутентификации.
- Использование моделей “*нулевого доверия*”.
- Внедрение *SASE*-решений.
- Использование безопасных инструментов разработки и практик (секьюрные заголовки в запросах, защита куки, централизованное управление секретами и паролями).
- Своевременный аудит безопасности.
- Использование сервисных сеток и инфраструктурных сканеров.
> [!note] DiD
> - Концепция Defense-in-depth основывается на военных принципах глубокоэшелонированной защиты.
> - DiD разделяет защиту на три независимых контура — физическую, техническую и административную.
> - Три степени защиты призваны замедлить продвижение потенциального злоумышленника.
## Сканеры инфраструктуры
Для выполнения сканирования подконтрольной инфраструктуры на предмет уязвимостей используются **разнообразные** **инфраструктурные сканеры**, позволяющие обнаруживать:
- Наборы слабых шифров.
- Небезопасные конфигурации и протоколы.
- Стандартные и дефолтные пароли, порты.
- Не обновленные приложения и библиотеки.
- Просроченные и скомпрометированные сертификаты.
Примером проверенных инфраструктурных сканеров может быть opensource-решение “*Openvas*” от сообщества “Greenbone”.
![[Pasted image 20260616205715.png|737]]
## Часть 6. Рефлексия и практическая работа
### Проверка достижения целей урока
1. Познакомились с основными принципами обеспечения безопасности стека приложений и инфраструктуры.
2. Узнали, как связаны ZTNA, SASE и DiD друг с другом.
3. Познакомились с инфраструктурными сканерами.
4. Закрепили на практике установку и работу с инструментом сканирования инфраструктуры.
5. На примере рассмотрели закрытие потенциальной уязвимости в Nginx.
### Практическая работа
1. Самостоятельно проинсталлировать инфраструктурный сканер.
2. Самостоятельно выполнить сканирование инфраструктуры.
3. Изучить список литературы.
#### Самостоятельно происталировать инфраструктурный сканер
- Ознакомьтесь с методическим материалом по развертыванию и использованию инфраструктурного сканера.
- Выполните действия, описанные в методическом материале.
- Установите инфраструктурный сканер.
#### Самостоятельно выполнить сканирование инфраструктуры
- Используя установленный в задании #1 инфраструктурный сканер, выполните сканирование подконтрольной инфраструктуры.
- Убедитесь, что сканирование успешно завершено и самостоятельно изучите отчет о сканировании.
#### Изучить список литературы
Самостоятельно изучите приложенный список дополнительной литературы.
### Методический материал
Развёртывание и использование инфраструктурного сканера Для начала работ вам понадобится docker.
Подробная установка docker рассматривалась в методических материалах к предыдущим урокам.
Запустим контейнер с инструментом openvas, используя image пользователя mikesplain:
$ docker run -d -p 443:443 -p 9390:9390 --name openvas
mikesplain/openvas
![](https://legacy.merionet.ru/images/devops/images/3760/img_03.png)
Убедимся в том, что контейнер успешно запущен:
$ docker ps
![](https://legacy.merionet.ru/images/devops/images/3760/img_05.png)
Контейнер запущен успешно, в браузере выполним переход в веб-интерфейс развернутого инструмента openvas:
_browser: https://localhost/_
![](https://legacy.merionet.ru/images/devops/images/3760/img_04.png)
Авторизуемся, используя административные учетные данные по умолчанию (admin:admin):
![](https://legacy.merionet.ru/images/devops/images/3760/img_06.png)
Переходим в раздел "Scan", выбираем в выпадающем меню поле "Tasks":
![](https://legacy.merionet.ru/images/devops/images/3760/img_07.png)
В верхнем левом углу нажимаем на кнопку со звездочкой и выбираем "New Task":
![](https://legacy.merionet.ru/images/devops/images/3760/img_08.png)
Справа от поля "Scan Target" нажимаем на кнопку со звездочкой и переходим в интерфейс указания нового target для сканирования:
![](https://legacy.merionet.ru/images/devops/images/3760/img_09.png)
Далее указываем наименование target и ip-address (в моем случае это localhost, если вы сканируете инфраструктуру, убедитесь, что обеспечен сетевой доступ по портам 22, 80, 443 для полноты возможностей сканирования):
![](https://legacy.merionet.ru/images/devops/images/3760/img_10.png)
Сохраняем изменение, нажав кнопку "Create". Убедимся, что в настройках Task подставился созданный target:
![](https://legacy.merionet.ru/images/devops/images/3760/img_14.png)
Нажимаем кнопку "Create". Созданный Task отображается в списке tasks, но еще не запущен.
![](https://legacy.merionet.ru/images/devops/images/3760/img_12.png)
Для запуска Task нажимаем кнопку начала сканирования ("Start") в разделе "Actions" нашего Task. Состояние Task изменилось на "Requested":
![](https://legacy.merionet.ru/images/devops/images/3760/img_11.png)
Через некоторое время обновим страницу и убедимся, что строка состояния нашего Task изменяется на количество процентов от выполненного сканирования:
![](https://legacy.merionet.ru/images/devops/images/3760/img_16.png)
Сканирование может занять продолжительное время.
Дождемся завершения сканирования, убедившись, что статус сканирования изменился на "Done":
![](https://legacy.merionet.ru/images/devops/images/3760/img_17.png)
Перейдем к результатам сканирования, нажав на статус сканирования:
![](https://legacy.merionet.ru/images/devops/images/3760/img_18.png)
В открывшемся отчете можем ознакомиться со всеми результатами сканирования, нажав на них и перейдя в подробное описание:
![](https://legacy.merionet.ru/images/devops/images/3760/img_15.png)
Подобным образом можем отработать все срабатывания, предприняв необходимые действия по их устранению. Полезно принимать в расчет рекомендации по митигации обнаруженных рисков (поле "Solution")
#### Список дополнительной литературы
[Базовое русскоязычное описание модели доступа ZTNA](https://codeby.net/threads/chto-takoye-ztna-i-zachem-on-nuzhen.84541/)
[Обзор эволюции концепции ZTNA в мультиоблачных средах](https://securitymedia.org/info/zero-trust-arkhitektura-v-2025-godu-printsipy-kontseptsii-nulevogo-doveriya-i-ee-razvitie.html)
[Практические рекомендации по первой фазе реализации ZTNA](https://www.kaspersky.ru/blog/zero-trust-transition-practical-advice/39484/)
[Влияние ИИ на адаптивность и безопасность решений SASE](https://habr.com/ru/articles/914496/)
[Кейс по уязвимости XZ Utils и практической ценности DiD](https://www.wiz.io/academy/defense-in-depth/)
[Разбор уровней контроля (физический, технический, административный)](https://www.wallarm.com/what/defense-in-depth-concept/)
[Современные мультислойные меры безопасности](https://www.fortinet.com/resources/cyberglossary/defense-in-depth/)
[Концепция многоуровневой защиты, сегментация, ограничение ущерба, адаптивность защиты](https://www.isaca.org/resources/news-and-trends/isaca-now-blog/2025/beyond-the-moat-modern-defense-in-depth-strategies/)
[Обзор новых ИБ-продуктов весны 2025](https://www.itsec.ru/articles/obzor-novyh-ib-produktov-vesny-2025-g/)
[Разбор трех технологических волн: AI-инструменты, DevSecOps, Platform Engineering](https://habr.com/ru/companies/oleg-bunin/articles/887316/)
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 KiB

+115 -90
View File
@@ -9,37 +9,26 @@ aliases: []
# Excalidraw Data # Excalidraw Data
## Text Elements ## Text Elements
mini-pc N150 / 12 GB / 256 GB ^mini_lbl
cloud-pc i5 / 16 GB / 256 GB + 5 TB ^gihc0eNQ cloud-pc i5 / 16 GB / 256 GB + 5 TB ^gihc0eNQ
Internet ^JF8gyxg7 Internet ^JF8gyxg7
NPM (публичный) ^De5FvlCO Caddy (публичный) ^De5FvlCO
Portainer Agent ^aa6HjhlV
ZeroTier ^m9o2XfZR ZeroTier ^m9o2XfZR
ufw ^irwhPywz ufw ^irwhPywz
Nginx (80, 443) ^3YCkaaEW
ZeroTier overlay network ^5VEs4mhQ ZeroTier overlay network ^5VEs4mhQ
AdGuard Home ^14mDr3CI AdGuard Home ^14mDr3CI
NPM (локальный) ^Oynv3oBy Caddy (локальный) ^Oynv3oBy
Vaultwarden ^0uUIYjrp Vaultwarden ^0uUIYjrp
ZeroTier ^8m3hcPU4 ZeroTier ^8m3hcPU4
Portainer Agent ^O5TryriF
Cockpit ^YY7Tjxu0
Gitea Actions Runner ^bC30cure Gitea Actions Runner ^bC30cure
Gitea + Registry ^K59um5ww Gitea + Registry ^K59um5ww
@@ -50,13 +39,25 @@ ZeroTier ^WzKFf7RD
SMB/BU/Files (5 TB HDD) ^HZKjRf2E SMB/BU/Files (5 TB HDD) ^HZKjRf2E
Portainer Server ^7rAi7eTS
Cockpit ^aVjmA1Ms
^Jq0JkEPn ^Jq0JkEPn
Gitea Actions Runner ^CM1Gp9xI
Gitea Actions Runner ^8PRErTQ6
NixOS ^DQNLMdIF
NixOS ^gTVW2le6
mini-pc N150 / 12 GB / 256 GB ^mini_lbl
157.22.231.198 ^eXMuxq0w
192.168.1.10 ^7IGqv1Hi
192.168.1.5 ^ho5T6rsv
Infrastructure — ada-dev ^title Infrastructure — ada-dev ^title
ru-vps (белый IP) ^vps_lbl ru-vps (белый IP) ^vps_lbl
@@ -64,150 +65,174 @@ ru-vps (белый IP) ^vps_lbl
%% %%
## Drawing ## Drawing
```compressed-json ```compressed-json
N4KAkARALgngDgUwgLgAQQQDwMYEMA2AlgCYBOuA7hADTgQBuCpAzoQPYB2KqATLZMzYBXUtiRoIACyhQ4zZAHoFAc0JRJQgEYA6bGwC2CgF7N6hbEcK4OCtptbErHALRY8RMpWdx8Q1TdIEfARcZgRmBShcZQUebR4AZm0ABho6IIR9BA4oZm4AbXAwUDBSiBJuaDVgtNLIWERKqCwoWrLMbgBWTuT+MphuAEZeosgKEnVuBM6AFm0ATh4AdgAO N4KAkARALgngDgUwgLgAQQQDwMYEMA2AlgCYBOuA7hADTgQBuCpAzoQPYB2KqATLZMzYBXUtiRoIACyhQ4zZAHoFAc0JRJQgEYA6bGwC2CgF7N6hbEcK4OCtptbErHALRY8RMpWdx8Q1TdIEfARcZgRmBShcZR5tHgBmbQBGHho6IIR9BA4oZm4AbXAwUDBSiBJuCE18AGUARwAtJIArejTSyFhESsDsKI5lYPayzG4AVgAWMf4ymG5nJIAObUWx
ToA2GZnOhMGeTr7ISQRCZWkpg9GIa2UatBG6iGYoUjYAawQAYTZ8NlJKgDEgwQwOBbUgmlw2DeyleQg4xG+v3+Ehe1mYcFwgRy4IgADNCPh8ABlWB3dCCDy456vD4AdQmkm4fCuNPeCFJMHJTx+FUOEDhZw44TyaEG/LYmOwagGYuSDzKsOEcAAksRRah8gBdfl48hZNXcDhCIn8wgIrCVXCpflwhHC5ga42m1kIBDEIYzVadFbzJarfmMFjsLho gDYJtYBOJLWxxYAGTYB2GcgKEnVueL2zqQRCZWluKbvrQfFUA7vmKFI2ADWCAAwmx8GxSJUAMRJLbHJLYVJ3TS4bAA5T/IQcYig8GQiR/azMOC4QI5YaQABmhHwtVgQwkgg8FIgv3+QIA6pdJNw+EUBH9AQgavTPqywRU7pinhxwnk0Ek7mwSdg1HMFQdvvyIBjhHAAJLEeWofIAXTulPIWUN3A4Qlpd0I2KwlQaRhZmOxsuYxpKHWg8E+8X5AF8
FmPIOsTgAOU4YiGw3WS2Sg06PHDZUIzAAIhlmh60HiCGF+ZphAiAKLBLI5DXa/lCODEXD5ob++YpmaDFYrbYrflEDhvI0m/ADtjQ93cIv4EtXZqYVoSFUcPWhF5CbBQEQIVCAFAJULhm85iAh6Li8ZwoMTCEZxLx+1dLzkAGK4fSE2WoS6PBdQACCRDKKG6DBHirSBkwUDmAQgGnCB0CSrieg5Lg5pMIaaDOmOVx/Kc5oEAAKi0lQrmuNKbtugT7 fggEMRrsc1vFjgdFgk7owWOwuGgk9qU6xOAA5ThibhrRaLY4THgl07awjMAAiGSgke4lIIYWRwmxAFFglkcsbCh1ivyyhUCWohmcyl0xY3MFAIMOQ/zzdqhHBiLhG1GFcdFkkxgckvElrGq/6iBwAbb7fg7uC0U20C38G3tZJQgAVLBQAAyTqvT6tggRRhkUfqQKO6BQOOSB3NOlSzvOdyjGgayatoZaanCRz7FsiwTHc6qoM4yyrBs2y7PsRxrM
oex6nue/K4EIUBsAASuEt73huCADuhAASJxnEuqCDPEP5lJIoTEYuAAy5rDoWxY8Vc5AUNJwkQBARQAL59CUmYFug6EQfO8D3gKCBbjcNT8h0aDrD0/JfusCpjIyQwufyxynOcaCOVc1n3q5TwvOySJ/ICMy4PMboeqWUIwnaiI/BFEgArgnTECseJ4hehIkmS5mUnyrKhfS7lhvybIfJy3LFXFKnCEKIpDBKUoykM8r8kqjZqnWOpPvqCCYag2F WdwXMQVwZmMiRjLChxjFsCY8Acx58v6kgPE885MW8AwMl8PyCkCuIQtCSQIPJ8ksiiaK6liOJgrJBLkBwxKktkSHatStIijA4nisyknsggXIMTyGZWUKpnmUykratKkjesairasqqJqtwSSancakGkaBQrv6lq4Na26oHaDrVs6KHoLgBweh2xBedeiX+mEj6oEkEy7rhxy7smTA5umvBrBVqZ5gWnw8Cku4JlM0zVnWDYFc+r7+pomXdpkBn9pF
mhadnoLggy4klDoavpZQNPeCSjLprpTmKCQ9oMCT7D6kHBpwzKHVGHCxhw8ZoN68wzMkKyDDMZo5nmm2oDOc6PGW8LEFWmTZLkBSjMUwP1GZTQkYcBmVAAUi+KzKDAmDKEsWl1GA2mjANjyNs2rZiu2nbdr2B1XIOCmjaO46ToZH3KY82BCM8BjZi2uDcItkDBMoUIwOpdLkHA3DcWtXlSS0clDtOSk6XpVwVBI9ByAA+kYnBIPyy2VIEVkcLcGt ZRrhuW6Bbu+6HlMWwTPEfFlBeAHxTed5sA+cW9QgcHfpU+ocNFbJCH0IgIKggAoBKguAbs4xAIG0FqcFANSEEYTValFr0AGIxTSREdf6iEAIJEMo1UQGIORMCyKbQe44OPFD+gkMQE7anoOS4E6TA2hI1T1E0rQshCjxOgQX5zodx3kKd52BNdt33Y9z3argQhQGwABK4QfZ8fxCHt2oXggAASgnPAqcTA2U77MNTv7/s2QFSpQSuVAupSgaU4Hl
XJNOzBV+gyeVc4zEJMaB3cF3lCcyKzBYF3DBdVXwpSi6AAggeJbFsuKQtCPUIuFXsQACOXYPM0d5UStVFbyDWPO7DJW0ylWlbSHKFZU9WzU1kjza1uHtbA3B3d1cKquqBTY2Ua4GoZY3yxNVo8AXP3F2gnPQODaCrXU60p7F3BLOsgyLIk6anSGJ1XJGIYXVdqDJjwHZr+sz25sE+PvUppblr91YA/1DZNi2b2DITj3E32vFS1hVNkxOHy0wfVyS HF5SyoZIOBj0CB9O8mP+ilezfbMRb2+c3KBWhdwCY80uoAeokfNwTuslJIKafi6BQhMuBbBGUbIqi6KehpeLQrgYzEIslKUiyxl0mZYquTH2pskKtmMbwjlAs5ecSgX/oedlCpKiqAUagHoWGqNFpWggBPrblI7JZUuBJBl6n16g4GdGbaDBh0utlPlcVHiWR48GM+x1VVvIb2m+YcIWaAlQtCZFY6XXBFNgEviL/WDT2I0FMOE+T90BIHZOEGGw
cw6mSxTdOy0URa5RDIMBVvgTQ+BcRa1RJDQ2XRthOQ8isbQexxJuXTkMFYcQEhLHmCsJM3QcHDFul5QSvleBoOuHrckbsyoe2RICbAyQEiLE0IHBKIdkoMNROQDgGIsQAzjgVLkicqRVToWna2vBxHZwTnnJOndmqOhLo8SUUIOpymCj1GuZ9BrvmGs3Z+jxzSnkmtcBInd7QtSfi6Eeb1bpLC2PMCeT0F5MDOl0WeMY4wrWSDwX0Mx1gT23q9d+ AUr9izKDAmDKGONrWey47gTU3AVJIZVOJFSWPhdeosVZoASreUWW0gQ7TVljIQvwDC1k3LgbgE8IDBGUKiGASsOTkDgNwIWwFZ7u0/N+P8l5VZXxAjMfWkEGByAAPpGE4LBbU8EJC9H6H7ZC4xjzaDajcRYcI4STB8v6Ii+5jgrGPLCLY2iipKPiHRF2aAJjBTfFLYSvBDi+3EgHIu0kQ7QgQJSCYziJjKTjmpbEMlQ4QChBnbAWx/FZxpDnFy1c
s56ZlG+pWE+tY67nzxlfG+Mw1hdgSK4x45MRy2LKL8Gm0twmaxIhIUgQhnBK2YKgVAAAKQAjCCAFYQQA3CCAGkQQAnCCoBVAABQAJQXivDeO8zJgrPigG+D8+AvyUL/HBYClQwImQjFBGC+BpkIRYkLfkKEojoVICNFuqjSD4Q4ERIp6ASllLkJU2pjTWntO6biJiLF2KsAGWgbiD8EACR8sJUS+wxbfwlvJfJn0yiqXUpUNGGM5bGJARwOA+hlb WS2JsoYsuhcg6V0qPnEeMo5SBUbv5WALxW6YjCh3IyXce4oMdAPCQuAkTuUymPYhIjUAzx1uGAqMZYQ8ASE07enAiytI4LvfeqAyzyPiKWLY+jOr1nPj1LBN91JDV7LkB+g4n4GwQgATVBjAAEiyACyw934BhfqlUg/wqCLlAaudcEDF4zQPCkCYgyUh3iQb3VB550FjKvowxWzD7m7Q4WBashsOQTFBgANSSHUIQQC4JT3QGIq2QibbjHWHEPYu
lKgf3U5lkojUINo8I25tHhfh4MkShlspFmztmQ4S2Kygu3uDIsKntAS+39jMdhwckph0BFHGO8whFyIkPnal5UMGZxTnQ7lFIFG2kLt3ESbV1Hl00VXZUfU4l6KblknCxi24SFwIy8VXdrGoF7tA1Ag9SjDzKGEK+aYUnzB9FvNxR0QIJC8edHxUxkirGtfdR18sXq7zenTQ+P0/o1kBmgQo6NOZg0aDAxc4JoYSFzJ0F89B8CfAAPIQpNVjeJl9 4KxbB4JsHgZ4HYKjWOo9Fa9Fh7CCjRY4Qz/T0VLvESl2hsJJCWMYo8zjFjuzMdcOWkAYX+3LsHJOEgYQKX5W41SCcvEIR0npMkJsyjZziYyMJXKS72WiXlWJop4lyrKX4TyySG6+SbukluIUsntwip3GK3c4oFKSo9FKEASmApHl6bV49hw7KDKGOp5z4hbEmAmElHTHYdK6Z8HYM01i7ExRBM+CAL6oF2u2SZd8+yzI6PM7h9A+H4GqBSKckLoA
DLX1wZ2FJu1bUZMBTYtVOTX5+o/gzJmLF9CsyiBzYGEBua835oLYWJSECi0/uLWSpb97hIAaUIBCsprKABgiuQSKo0ot1vrXEk1HEm0GQSiqIlyVHFJdwTdVCF1UqzjS7h3s8SdBiskNh8VmVH1ZWlHKWVcArC5bnHlYrD38qkRmAQwqX2irEY1PwRc9XilLjK02XUrjaMVaG+ukBG4GNVeNUxVpOiWOIJKg1yLjWQo2rmnB20VirBnnajxA8nXL HSOR0MakBwExqgXuS5zUCXxAIog1hyCNpoO2mwvq8smFzhYWtL5tSfn+jTRmrNELdl5pppI1CS0ZFoU4osY8awMVbFqtqVRxxYh4v2ISg4xLSVlHJYqpYsQ4x4RxWvOMQUFrMs9uY1eVjPg2KDiK3l2AeKos0IK+OmV71QTFSSCVQSTKqtlZZGJ1kFVbyA05f96AElSmEEkn0KTdVpNUSY/0bdwpoDNKa2KOVHn92tYPeIDqspOstXlaO3AFplmu
3vNajYgT5jXxCb6sJwKIRHyDafIG4bQZ91ndAWB6NIBjuuLgdYfEABWkh8AADUM0YyzVcXGOa2z5seoWhIxackDr2RWvJikClXEZszetbMm3oxbQgHm2A+YtAFrgIWryu09seF/H+A7/4mqhXG9ARgoBTryJrZFFl502TgX5XduKlhroFRuklXyujO3Ra7Pl9DUre3TNgboGLIkcJZbStK2AEhFgQE+3U+URU8n/UK7OkiM7SPfTnER8jysgolcB WGmt/psxpiLGyhglUd6NWmikBa1at2RpGdGl5LbIADQTcNJN6Gi0QBLZAi5h5K2xhoyte5JGVrPMwa84RB1RFCGcOm5gqBUAAApACMIIAVhBADcIIAaRBACcIKgfUAAFAAlFnV671Pq8gDpSP6AN8BA32nOZGkNKgw0bJCOqiMCB+dRuja2ZRsZRDxqQHuEB/lApBWCsmpAKYcCpupqFmntO6cM6Zyz1n7Msk5tzPmrAXNoDoXc2UksL2BVlm8pW
6V0pZUiQg48KDtcYO6iGrsoxmYNVTXWGhjDzbDXYdNQIUeA8Zgdm2umNTkBF7HUFWUVbzrLr3n8esRI8o0n0YQHvf1VwonH3+rE0NwMI1cfMn+WN/GQH6HmGwHgAANPEAAtVi0nMZ1FgxAeTe880dlvj2e+ZMNODa5pWxjETID6brQ29mPdm2tos+2mznahDdqHn85zj9B1hGHSDaFlQhB4goD5md5kdZooXbZMelcrhhYi0S3d9tyH+UeJS1AtD Hbm3XzKOQCgmsJDALnlww2HA4D6F4dplkVSIDQrErCkY8KxjaBomiiYDK+lcYgERZqsQOIHHjNsdY8RJirZ3dcat2g10HGYrsNCi25OQA9kJANHMpucrA3YnlYdHEuNcbHIV777G8r8QErYv6QlV0A8q4DUTloCmsjKyD6ra4wa1XBnV/o/Kqn1YVZDZRUM5Kinki1Dbe1FNSl9jVjrkfOsHK6647rC5kYVGMCskxvUEv9UYiNTH6qdNYwqZq5Yl
s63pPWehAF6mWJRvTlk9OUEiaFysV+Ov6yslQq+yKr89Vc1SV7ygDSiNQgdUWXcDWjq7Qc1ID+DA3sn8eG9cVGOqrHKLRyZybDmzWzZEl2ebPZ1j4KdRrjb7il4urFL2ce6xVMJGClmHex2q26a+ixmJIbNQ3c44anjMaoZPcqIQUgFBJAdJgBQIwf3ZM4wviDpJd9SYlqJ5p2H2nicI4gEjlmRnncZLM22qzHa7O47d0cPtUBf5Arxzhkd8sYX+ toQ5zWHjMa43akE12RNMz0OP2HM/Gcb85cLIkPWMYv16D4GBAAeWATrY5/oJPnPLdJqBgybskIU4TpTTbL78ehjg7m+h8FRCIS60h5DKHUNoaQYWtP+JtuVnW2NQFvl61+ZUIwUARtyDG7mybEjtQpT27LY8G7BmotnQulRvJVgrEWqsRnYaNiwgMXZV2Ac7tex9o9v2aBb3WQ/T4pp2A17TYE+44Vf2w7YHiC2BAixgew4sm5cHxdIdcqH1BjVs
EwDTvz3H6cuyZyFldc32fVbSdFh2fk4v7v54loXEcViaE6NgPEo2r0S5+ofgExAL05Xl0+Er2u32a4QOr9b37ZEv8a5AQUQGncpVQM2tjd5Veoetzc+t9Erdy0bdkNNUisANHcFoJssNB8ngPcdhrVVNux/dP8GAg9vFttPRlh1g8FNgv1ygfU494cA1olLsU8w06hbsM8Hts9gFKgEgABNT4N4XAXACsOkMvAHbNKvJTLsCHWvdTevGHFtOHMfD HvKpPR0hzJepjWicw+a7DhS8PFLWIRipLqqk1LAHPAQ9PqnXJ4omHgWeyh0baRmJlWZmMNT3sGy/2xjioqF1G0X4yygS+IFM++GXOZJXcbRCbND+SofQLYNgHgAADUpAaB5l10P313GlOVLSkxSF2DpVqzWkU0gHvAwTazuGwAdzwQIVdyp3d2wAoW/CoVwBoWqx93oW7X93eXbU+RD1YKHF7UNiEEpAoGjzyEHTFHjyi0gBShJQmFO1amXRuBDX
ZWtdvRtTvHJbvTHXvbHfvcfCfCSYfUfHTEnNzQBKfSoLzZWbAX4IQZOJafzRfeLZfVAQJJIMgu6ZMB6O6LYShL8ebJIZMJMcefFfBCeVMfkQlarHYPwngVJNYLA/BSgrnMlXfGhA/KXCOVLdLcXThG/PLArRAx4AkRXerV9X/EKSrddSg92UrHXR4f/SVA3MoNREAndE3BVCA+sZVBDMtJDS0TVTlB3dDPVTDWdKbKqD3M2JxXaYYSgzbZkHgBol kUIhz2kOOA4hcR2DXQxVL1Ll2ArxZTQGr39A5Try5Ub18Q4gQAOBfW+zfXUjMIzkpHiE0EzgtGCUn3h3niDhAwche2FAg2Hxrg60RzHmUTKDR2bkx0XzXGXxNDE2iiw3rT7ggmJ1tXBTJyIwp0qUhQPyP1ZBP1gWuRLGLCv0gBv2qhPgfy5yDUCg0M1Hf1UNPhFz43awE1vmE2lxNFl0HHlwQkV26OV3QEIFIAoEkBsxgAoHdALVKDE0N2mmNywO
bQgrbFeW+ZJRIDseYI7E7atSJJPBgusNPPjO7CGLPTjATToCTCsZgGYfQSQAART+yhluwE1wB81VigA7nYIzyxFeCoGBmmzJxMxePeOVg/A4EIEe2OM1VIF+MeMOKBJAVePMMsOEBsN/H8x+LYD+PRn+1KEB2B0SSUx6G6HHi9TrwpgbzkKb1OxrQMxR2My73M0s0XGs1s1QBFnx17X+X7SJ1c10NHWe3NEIFVnVlp21lRSX2CxEnTBQWTHCx4E2 2BxVwPX0bSINtxaPt1wSdwoLQGIWoNoLnHoMYNQDoT91bXYMD07S4MP04XDwkEj14WwHBCEECJ6NEQtnEXEIgBSk2ESG2GMXjD3GMWcUYyImuUSB2zKjQjXTDX3G0MVWYghLRSXmPUPSh3uAa1QgDmMIkl8LMOb1b1fQ8UTi0m7171wH70H38Kn1H05HH18PcLByCM1RCLnwiLdm1BxxNVyTNXySt2SM31SiB2g1HidSyN2RyI9VdmcR2FT3RLKN
FmDwXSX6C6FuhSBwWXR7GSACMfEeHCIrhZ0c23RtnlHiDWCWF2CWGiISCj0oT5wFyPWSwjiihiinEv2yLSNv00AQBmBymfWKL/RVzNQkQqMS2qNfya0A3qNaw0ScNaPAN0QKP60MWt3KFt1wH/DGyGNQJGPQPNUMiwNuiVOClmOuhmKWIozHjPVoyWDo29Vj22IT12MDWTwOI4yOOBPMLBIhK+IxJhKxLhI7IRKtGVjgD+C2WFH+D7O40xOxKHnL 5B4C2EDR50KnLG2yWkv1W2F26hUztz/wAJE06OAP6NAL6Kp24TGEBU7GYAmH0EkAAEVkCj8eCLTDZcB+Eo8njhBXic0h1SQDlnTJxU13ShDPTSlTTc0Ay2BDlBwXSQzB5PTeE0YOBCBwDqdil9kYznTUDi10DJN5jjElSNhljEicMCDlNiDsEtjndCFdi3cEAyEaDPcGDvdfcGE3wA9Wt1jQ9XSICJAUzCB+FBFY8h0xC29vjApL9TsExNFiz1hG
zKAJNzRvmJM6HHhVK5mhzTNyTfgUK5MJz/hlmMMn3JwkB7OVnAUgXn3u143aE9HWHiAnnHidnlDNn8SWEQT8h7G0Ah1ozwR6BcmWDCPXV2HmFlL9HzTTEtK2FIRix30YniwPTfxv32AjwQEvTOyy0l2PWgF4X4WxHmQbmf0DOVzRLKLVzDNqwjNKLqJa2ALjOGATJ0SVWTOgNTNgPTPgKmgACFszADKSCzMEvRJ4Ng9s8DeA9hyMQ8RJug0w9s7p dzcgYSx5t9w9wlpCj1gOcjsjFjFz17sjF0ImcSUoE0VKVztr1ntaTuVSSfEI4o4mwbDiSzDiBNAEAJgM4qTc41UmTocx8y8fDrzGSR9mSZ94NUc9UwSojsluS8deSCckijZBTbVQYd8xS99sizjj8CpmJtEpgz82deAxgOcyjqi0A4Q0V4RNhGMdTRk9SNiDSpd+wui3SegOAPwABpGzOAUGNM7ZcbaM2Mjsk01i4pJMwc9MgSrMoSvXQtMBfMo3
KEY9Ql9zE9Wz9jWKlzK9CSwcehtoSTqY9zDCW8HsJAAAdDgHs7wbASpaMVMZIVABQaU1AAAcR4scooXWFcr4t1D6U4kGV8tfHfE/E8XnBaBWVmV9iIsWNIGgncAitRCQg2SvDQinJgIlAOX8GORjXMssqFOstsvso8r2G8o8rQu8vuWYjYg4heXZK7XeU+W3xEjEltEoDBVyqsrgBstQDsp6GKp4FKqcvKrctxCcwBV5J2K3JkKJFJwFKtGIGUCE WaIqbiDYAOVaFYp5G3YPVTNglrTg9hbg/rSA0gOAOoZoXAIwAfEQ82S2J7UdNUrYbQfbOERMMseonYdEoiNCaQyldFPcDFUsOMeE64Q4alGiXYQZYsBaYsRjSvcxfbS8kwvErvHxWEeEREIkzvN7aAL9fSckVwv9b8gDECv8ukgCpVTwmHakjwyAOuJ1UIyAcIjHIKaCmIjDHkhIh5DfF0YpUGAAcTQsyIwolKwryIKniAOE4hJXnQas503mniWB
CxGIE+NMgXwlIcKlMejiGvjdXku2G903IgGcm2BQSdgUu7EejPRAsi0ni3252SKClSNwoBFdNHg9Oyxeoyiyn9IV2ETqkjK/0osi0qJ/VIpqKjL1xUUaKN06mYrNw6LYpVW6Nbm4uuE+H4pQJdzQM5LsUMl2D2k6GvhIRIzngHmWwIPtSrLFH8SwX2FTE/IbJUuMroIu2DXbOYPT38zYLOJAUenrVIASE+BVGELxNEJ0s7FmDxS9AfgpNkN3PjyY VOfxz2rRLH2Boq/2aPjUl3aOYpEt4MgKdCHMzXwEktzTAKmLABmIUrmKUvVNUs2i0rF3PEt0QoVj0qDy7RuJ7RHENkHN4ROtHIVxHUT0ClCtXiVOgQ3QTGYhKLW0dkct8tnX8t3BJW3KiWPEctNyhsZykPNzit5EYxxPryFDMMUgFSfKytvMJF0m/QMi/NCV/MDgh3KvRIiWAteIm2CPqrZOaqx0gC5JXw6rXzLO6ptVwCBUGuNHwLGsXnwnhG9Q
1byUMMxUP1XR3UOZKgFZJxx0OmykH0Jc2PP5NMIkFhXhVyQIDFOKQ2sZylOmB2rlP2qVN7C/NQEVLOp1OiMuu2E2ItnXTurgqap5wpSQv31qxv3pX9iyM+udMjjxGjljj+pouDKBo/Wq1Bu/3BsBoFGa0AIWIgCaMYs60VFN3aItxTMQ1Rt6KmmzExoZNsLzNxvdzelmG9wVJ1IktU2kuILQAel9Cj3C0ZuMWoKbKVvO1Yyu1T2HPRO4x5s7JAVT W0IuIvlMf252WoVHDQSHhDDUaN1KrImR2umVxzQMmgLLupUr3NWK2rUxpgHKOu8GwF01zFmlQAUEKh4FQD6oACE3aiK1gvbvbHMchnMvoXoch/o0YvNxgfMoBwsAsDI4YQtzAwsIYItiAMYJyYtcZZR4s/qTKzKLKrLfJ0t/Ass7b0BBzHbnbXb3aUhA6/bV4A6fbSsuZeZ+YqsTjmDasJZ9DComt3INZssIAq64AnbUAXaDw/b66fbG71hA6WR3
RgA4HoASDYB4oGH+MXMgGXMUzBwOuluHukLlp3PkJZr0xVvpNUK5k1qxzZI5JPL0O5JHyNqHUfrmsViYnwCgAoGWuyFWtnrp1tqC0xRINlL2oVIOuVLdrrM6E9ouo8Ouv9tuo7CDoesQr30dI+Ejr9mjo+pwrjvZSTqfyKIBtKNTiorfxTvIrovztjPayYrAJYt606PSurrMQEPruvqhKNXzPGPOviIVK7ops22ppEmvhmH23dS2MVpbwnrbPY05 qPlPqf8CDXr8BeyjLiliBlAhBSRiAIy/TRCPiOV7Kio1gZyL8yIph9xZqiJ39pDEx9glhWJ5EsDgrp55FTtL9vVKU0JZ0Ex9yq9sSnskrryzD7yyNKbftsqoQU405PyCqQcfySrmb/zS42aVUiq4cma6qKdZqmqF9DUl80NYjV8+TEKnRkKJbTqRTydfRhq3VhL54T9jwlpWIH7CKltlTKiqoyLeADhmp1hUVFDhl9b1jtr/8mLk0w9+juEiondS
qONYLvLPPQGSCEAAFUVQuCRNSAhYt6RC5NtKVzxCpbkgZaodprOKFbaCL66SO91aTMMctadbtD0CxqeSjy36Ta1GIBuyhTra50GcQH7yB40xwH5TFTDqYHUx4HvbEG/b9SA7UHP4TTvxHqEsI6vSMjOgMsIRsLr8vTcjcBCsAyyHU6KL06A8066sKmaG879d6HQDIMy6kyG5K6Ub1U0bcAXwuGnHAGpg+H7FLTrS3VcDSa1sjURHKyZLdgLTkx5g B4hgR9Qzqh0Lq4zczxMbqdxCzLa1L17Hq1jtK7dSCaydjKdzxGyPc6CvcmD2zuDbsuz9KwhN67j0BBtht7wCAgabLPiJyUoip1EmlYQDsCUiVYwlDUJmIVg154xDwoEtz0byr+kL6MV+lNhywjx9hVsCasTErcTwGUqoQPsXFMqYHbzfFKR/FAkkGObwkvD6SgLqrcHub8HeaiHOSjVSH2q4LOqZaqGerUog66GMiGGqd99RqF5xgZTEx/7CK4xV
yTMxR7ZHWbJ7GD4TBno1oqPMIAVh9AEhJBsAOlNHGVDGxbjGElTH97zHLHySq6Mkz7m9FCHG1be4XG77daPHDaJqfHTUP7x1J0eygmAsQn8mIBJpHokgCaz1Eh5tLTrUj7IAvxVg5g9o0lrSXIeh7pKCDTTT7rhIQ7IAHTnrCGRcxd8GimXr70VhH1ynRFKmKGQbwyf9KnaGmmGKGGS7IBut2m4NOnKY0yTEa7rg+J+nhiVphnDI8V5jAkdS/dJm aqjVTL8rklsLyxG6KDbf82jjaZG+zfrKgtcYAOB6B4g2Bva5h+Lzr81NG5KTkzbFLLl7qraXqg8ZbCCbb/QzHHdazKCrGmyDioAji2yWDvrdLl6riDLwW+yBiGBOZ8AoAKB97shD63ioUT67LQaGckhqUJqEh5F0mCVPLuASUDgnKAH+lJqBdyptQdzCpOIgHzFDCyhibTDinSnnFym7DimAdamjI3CWnUGIlvCKrSq/DsGAjEkkdZ8EN59Ap+ad
QJxKlWxGzYyCUwkxkgkmVnGy1mzs9j2bFHTzsa57VG9nU1OhCJSAYADk+mLmwB8STG97JbJGLHkWW1tybHnmaSyg29VbUcBm1CmSvn3Hm6h9n6DDm9ZrTb0A9BoQ4A1BQTAmbzxTAsIWoWthtBtgHJJ5oj/DtW3a1gkhuwdS9oOw3UkwC78X4zCXYsMGUjsmXrj9T9z8Y6CHw4fYMLfZ2Zk62XyLmXP1WXs7aLGnobIAi7uX4by6oDkahXOKRWOG RumTaqR8cNLcMhnbVvb7VRnd8JnMLmHsK5aN1iXL8FnMxaM1b+GH78U9w9atmJHxddnADjSU0lduEDghAABVfURZZoEytR4G+cS666x52655/R0srq62+ikgsg7Yl3esqg6x5s2x1s+xsF3Ipejgle6FvrdxibLEXO5Mo63x942yhPOFBURaJy5dDdCassJpEsxdcjacydUJsNTUCaxjel4xPQzE72EB2vQpyq0m4pxYTQMYbASkbfaBnl2B4gKw
RaBjxtTWpXw2MDW6gl/QXIsEJL5se6VjVNZgHoHIZG7G1L6CjXrsZ7G7bzTiF7KguCuClhCIRNMAhBUgHWnXrmXWrq3X7nj7HmtMjKXn7HkdHGPnb7ND777MN3PGX6/mjDfG9mSkOApzk3wTQX7C7bQGxQs2c2Zg9p8UWEnZvDuBFhHyXJ/yNgmK7SbqpFbY0GiXMnkKQzBcvSW2z8L8sLr1qW46785dfqSH/rGWB3QyWXqL+3FEADOXDcwM4amG hwhm0HYVxp1mifIVzmvB2ViCxDBV1qnpuItV0Wq1TV3AYEKW/5o+mnQ12W1zH1WdfbAOBUhakiq1lZovHYai+13jWNp1oTPZoAt1uRw2TQYECa0gwIQN3ommEN+SsN3Ri2jdV5+Td5/kkhSsx175+Nv5pNgFmxw4uxruhxmFpxi47skxlg/Ng6+4qPCS6y8t/xlkFKfbXFhMWMadGiYi5xVbe+isZIQZZqTCS/Saw7KJWEftg8wdgpkm17SpgksY
EaK72LgO4DRXcAYYJXcz13H6Zs3o8VZgvbphSyliuhKFRG5ncFLToi1hz3VKWyr22Mb2lG12Tjdmc8JBNBPgo9GZAhRbHXxabnXXD7Za1OqTQPfXEdL7IONaQ2YPvn4PfnvHkOAXY3W8rDiARThRsPgGM3uBCNwL/E7p/F5iiZKCJlaNtBCbhgdoOwbUGPqstg62B4/QFh5gOvc30xx4AwAow6sGktO3cmIWg4r9Q4vTiB8t5gsoGWGsmWJOh2pO CclSWwzxYpnvPvYuqKQVyVmk0dsqjBzd3TmqrmlknmuViIlq4h6Io98hhC8spC892sK9/Dm96eKZk/OERZ7bRa3h+jIxW5Pzp/bpPcPcSjP1TZv97Z1owDl1gcED0S9ARYfQeISQbAGzL10nSM9R25kBe5g3HRwqTAl5gx9DxCz5/9nD8xxNyxlaFNoFkFjN0a7Ny4trNx2j9Ab0l44c2UMtjFitr4lKAZOIfCQRxMIKWBElgwnYJy1eIKZeTiNd
R32Wx2xRmmK5p3+X8RBXKTF2rQABpbTtzgeaVtsCeNYCxis+1T0D1yz3u3gW6JitMZZ/jVZi9xztm5z6e1zvxpE7zFE6wyE74gc+cvTwE37scic1KpgIH/s2E79oLv9tcjcsLrpkD/VxzZLsfGNvx1QY55IBAaMB41NnZxdAr9U4rvxLBYYW+Crlo0SbYFMe6B6erxV5JyLbaOYeYv0dc+6eUVYAuxI5ke0/rslzt1MJ2BIci0bz03CtEPhTEQi2 D+1AZxKTr2L1dRbRbRQlJpGMWlow0Bkd8V/EngFvJT7ltThd3vLYNOFdlBzmkVpp/TiVxm1Bnd8CsIyCjJazmCoWvpkW6Nonahjilz2r9FyUunSTMNVYI4JZ+ahypa7pSlWldh5qX97/HSnZ2Lo0+L2RxL21JMrr309F21aSoMlijriATsdZfUQgTQQgBANFtzvZQM+Dh5s5cNw8Jaf+83dS09zS4x5684j6qF1xwygtjimAGzdZCgQFLXTm8bcc
bko+b8oyTqh6TnVKGtbrllprrNpzSgV1TtH9TjhmSfpwSzAxYWjCPPxCSwYSeQ9+8B398lMUS+z8+y9z7qepgk1iH5EzL2H2ckHgLn9hTAmIkix23im2xhz+oE5DL1Egq1AQgToYqrytysqjYUqgAam/FQEIh8qfD8tqrxUCpGWCvGVCt/HCqAgQjmVxCDDitgjr6aCSr0xSu2TYf2UOWyo0gB+IGT9T/T8Gs8tz/z8L8qseRqq4nqqsY+XSZ+Uo lj7geRObLc2lHiYsJU4iqJ1ANCclpaI8SahIYqeRHhslKJYxObfCfpNeVZ4vVnUxAdhKmvaxdl2BngSkKOLludy76m3Kn9Oprdhplmwzhk4PkUsClHT7/dg1LpkhlViAeIgHgZlI3ADinV9IvV02Eau96ZjMX+/X7bBZiaxHz4YsIomibUzayrrHo2uLinw5sSh4onmDzM1nuM4M91w2VQNLg4BAXMJ065nLuDu56YhDjnpDitDyw8KNj5rDqj5r
VBQT4H6H7T6crNlH+Gvcrz7T8n4J3GpS5b0yTLRx72dUGaFwAAbvbTfBbJ77o6/NJK+p/K7ds2FEhq+Z7uoa+QaJVSeNLwUMmDbJ6k2zjrDd22/HTtpoB4B4geAmgTCgURIr1NqQC3DOsOxQFa9ZO47QurDTlStM2iW3S3BxR6IcMAAskd22a8MN2QlMUMpkSCuEZm13OUEwLOhiMiazCQmjq1e56t3uzGdSte2+5+9b+pPdggJn25nohA+gToBQ SFtr8Xyn3vx9Afof4RG5kGqt1ATiC+g9fcBaLbKE3X6E2bo31FatXcbRZbjJuINK4ihMOou1p36Tq9V3m9d3yp8mpSX3kkt4hprip6aQfYzkzUe4btw+4At7m013Yx95WcfFDMq1gpSoT2gPDVuLR/Cg8Za+fb2Kih2CxhBGXDDdGX0Cg3BBGE1TROjy+Z18pGu1VAXmUQ5Fd5iSpFILPxjbRdh05iaGM8WIDV1UAhAMYNPWbq+13aTdBugAGpvY
FB4yYjGFeX9hH1ubbRnEL3T1tY0MoY8/WMXd5nFx7wsk+8dVAfEl0jav1Uu7mTzugGFCLgB+N/SNEA3TYP9UAYOc0gRkIwph5ivyVnGPAd4aklm/obVl6GK6NcpghGbQHWUka2lSORHcLMx3rZ9dMGYvSKNFHeq8cxuXCOOt9WyiP4kBpDMTqgLV6LcNey3BptGXorydmi+A/XoQMN7bdje87UgVaGjCUCRB1AvTpuxlaPROwtGQ7EqzbDcDKabA qAD8CMyMhOYBYrmcOlAEjqAwY6ttOOmnQTqwxgslRULPgHjr20M6XxbOnFgSyS9pesveXmlgyzl0eBRPAQUIJEEN1xB89WetIOEFyDW65WDuoLG7q1pe6A7XFp/1rhD0K6vAn0k4OEF11RBc9OIZ4NkHyCIWObUXhsT54PJ2uTfdAKoEbAlI+uE2TFpWxmxoAzeI3IsuNzmiBds8qECYMsA0LloyoeiZbpJyZbjAh2bvZKh71O6EkABZhTQJ7x4C
mSisFUyOJCMuCD3mBy94bMOawghwe50hICZmAQgaMP+ArBcEAAjiLQR5XNw+IkJJDsF9AR5UejQl+NSUmrK03mgbKDvF0MFaFjBetQ/l42x7v10uAfVEvYJ4ZgtJSeHcRswm0AbA/QkjebD6AMo+D7Ip1ZhLdGYT7AFSaYI6jW0DppMgBxLPdI2xQo5MeAaWPJpAPG4vUSmZTPtqUMKHA1ih7HdkNQxk4xldeinAgYmTqHEDwue3TVB0laELCTuN aBrCArQqq9we7rsw+zTGAduzgEfdGqX3JAdjhQF/c0B8FdVgKXPbrJQe4pJho43vaa06hCQf4nDVfaFRaUZAhUH/Vh6LRzeI4GvlwMYoMDgOePHPkG3TLcIOKHEIQPoDGAUAZKKBfLqbUn4sClKuwS2nPww4VcuBPzcgjVz2L1cWyxxU4nexa6UcvqNHbIRAFlBzgieaLDMv12Y72VOI0hWGh5SnTlhQSpLXYNoE2BG9ioq8ZnOiXpZepYg52KBG
Aj3EmE2DhYUk9vbwQsipoyU9sP/OUlMKi4QB5GGlFzvMO+Hz0RyEgOkEYH24vg8QSwViHXV2GKD9hoOSWqoJOFWMT63rC4c2Wi7XCG6N9O4drSMEP1dCEbQ8q8JQ5WCpRCURsMwC+EZ4cOoTSAJNF9BFdewVPMrrTxgYPRqu9Nb/qzwpo1smOKI4Oqx3DqYiXqEAqlviLjqEj8ixFfIXN3E5FD0BS3TAbrmwE69KhxdTboyJ26yEWRU0YnkgUGKA JygT4VYqfdFluykO5ycby3icOJHCgbi4O8FTYUfA3TguFRhyDYqhMND6KpMGVVGYdK1ZIWc+ah7RPsnwoYOdBm4tXMDsMYa3t9heAvnHNFlLm4zhjSS4fvz2wlhSwNA2vjF3r449G+JPMAtsm4TMAhAuYUGJ2EWR1BVGbPArswLLRgjYSzbN5ngShEL8hekAWEQmzrJg8SEiItNsiOYLNdnGubMXjCy3roAW+fA/EYryKGDdAoQUFdKsCVLbZRur
FJWQzLkVfACJ88z0R1Msh1lYHB57utNc7i5HUHKUGMcfKUYay+6+9webQzPB5w4ISA+IX2fbiJlYhwCKwofRHsoP1HHD1Bp/M4U81NFK1/WV9INlaIME2iHhdo/WghyjZ8k0ufjccrFWh6kBlYYQUgEGFy5ODHC/o5/kGJp5dg6efdImuGI4F1cgi0Y9dLGMAHxiQBWTJMeS3PSIDMsfHNMZ21pb0tiRhYt/B/hqwlCMJkNYsUAVLFTslOM7VhiQ EOGl5VYinYpg2wQ9JqRjAtDGW7/YBrJx/7CjFOynCUfO0qYacKSWnKVDp3GEh90GyoozlOMj4yt5hEAQhgex+5tVj26w/npgMHg2ZjR+rXPmaJPw4oNgqhVYNaLVrkDZqpFVUroQSBwgS8kXDHvqWdbuj9qzPbgR8L+RGAOKv0SkMcB5jOdQxwIjAvMXBHftIR5XeMavU2K/MLGCIwFkiNBbZiKOLjajrcUp4qc1wzAEsXHjLEBMVe2icoWN2ahV
PYZWhfsK7HMsd3aH2jOhO6IJMsCI4TMhRpGLsU7ymBBJfcFjWjBKMuHSjBBE4lgtzXNYuilgpAf8IQCWAIBCIxINcXsLEIqCtxpwykrH0946CLR3DT5glzDYdCrx5g8fLeL2bxs3gibf7oHxJ7BNfhYTVAF+Mp6ldfxUlCES4JlJkEHIXEvFPM0GExieWUgdJmiNJZgDO2XHNtqmMyGds3QPpIsMryDK5iyR+YnCQUKwE0jCJevUurUJYZI0uiu4 CpuPSPcLN1YgJg9wi3R3hb3Kp9s2hBhDod/y6EKceh53Poep3JKUkwB843wqKxVHgY1RC4jUXu0QGRE1xtnYWnqLFqDwt+COUUkNX3F7CyOBwwqDikBLnYz0QXcoqQN0n8MKw4TAlDxGdGPCXxHRXHgc09HmlKe4sBoBxWaA8xKQPATsDmSBFMCQRUCd/HNGYjyJImtaWMZBKerQSkxeHVMfsUQlNdUROYtIVkP7KpReEeMKPKNkY4s9sy9lMlrr
obD0zklUSGxOnJsR0NoEdY9g82a+FHnt5uoOJdAvYLdGWDWo+JZo0cQIPHFbMpxCo37hJhEz6B/wgwMgXkG1FaUlBBw8Qn6GYS+Sdxqkn1pcMPGxdnG0He4bBxMF6Ssexlc/i6NeLGQ58a1cyHOWcGWlV8/wwEaELY5bogBB7RIRiIpHYMvSUdAOGFMPzy8CKgidCUlNqxYTM6lIzXkWJSkw0FO1Q9KQyMykdMGhu3DMpowt6yFSpZ6LBD0EkYSU w16nZlua3cxNcl7FsThRnLLLr/iHF+8gBAfUAXKPqbyonu4rOqdPkXHR8FhsfSSfHxs46j0Bqfahl6xwEYc8BHEJ+rD0Ipw8WMGtVAIcCiqDJ78vaB4dhzoGGlLJobLyVDVgQlgpgc0tDkFIc7QjFpkAOAGwCdCWThwVksAE7FKAHBhwRaMAOdMulgBrkN0rRvgFCBQBQQ+gNGDIEjA2Zjp5ILcQKFJBQBvaVDJ0MoA2EYBsQIM7EGDIhm/AgZoM
ruww+7gIw67bRmp49McT7zD6KSiYkhPUkBxN4RdtBkAccuaE2bowJxYAVyKUGSDAxYMYAambTLADzYGZ29FtOuG+D6APwMgd0B0jYAUzwuzwLEFAB4omJzQygcLtkGIDiyEQks4WVEFir/gQexwI8NLIRAqzfiasxEiH35BBAywFAUmQbTMFIdDJlg2cVNDeJeZPRcPQco4SOmnTnJzXJ2ezykTJAUgfoM2FHiI7bAJelpdQULxthLB4gOpHdqBJ aSgJDugQzsgxARGQcmRnukyedwIIANAoC0DyOIvFfvmILYeltM4ZAoYJWV4HwJquUltruXJY9sok5LSajrQmqLQb6+wElLkz7rFQ4gG6KEudgJSwkiaAovsdCAHEXdABoqIkHTXyq1SI+AkhqWgwrj+ENwzAASLMLM7tNNRnTZAQn0YFJ8epGHA0YPC9aZ9FJ9DOGfkTLCNJtJ54rnLyE2B2ivUK8csN6jMkHSqgFkxPrMSn5zRBG86XnoY04Hey
CIi8khQUwECmPSGy9nSL0xXm9JE5UjEsX0jAeZGbDMBjgZQ7XgRIBlVCOs5Y0GUbznYQyemQhAqU6BhnjFHEebfFCTVYlk1eAwFVVjJTUG1dCamMuRtjMYK4yJaymSjokBUny1ZpLU8mT72BjMzs89M9GIzOZmey6ytvX2UTR9ABzs8XoUOQEWxbBEHenQdmQoJyRcyDAvM/MALKFnEyRZsVOWY4D1gazZZEs++dwwyDBoRo/jcyaiVjQQB9AbAU jpJ0vah0Hunvxrpg4W6fdJkT3iiBnM1iNzMpTvx+ZKlIWTCVpRjBnpHkkhG9I+lfStwv0k6XDKiCkBgZoMgYKjKhm1zwZrnSABkGmQJYixPpbNCPTYCPRKgEIenpKgnGYAfpf08xInIPAcDuiccwtFMyrlx0kZIQezrjOxAYyYyWMweDjO1B4yYyhMqQLFK4H6A2AjAdZCQHLl9guQ6gZaSTMxEJSCeDxBjtv39Kby9+UwdRP8W2zHxgS7UXXrjR
8JUD+CaA1A38gkJgH5mCycQsoumYCLfKzzD5JUpWQBFVkhAyJjwGWVrKxI6yrQesq4AbKxLGz9JZs/kL/MYBkCSAF82sAyHUAbMnRRkraSCRBaWTrgOCv4dsBDmuE3U90boV4TdrQUzqe2fFG6lJLjwzpHWOBg73myeTFgx7UYfEPsgey1gqYaYJLwsYphCZJLUXjHLShxyvohTZCU0HwrJycQ70nMaSOqb4EqiSubObnOpEVDC5ZY4iUQMrHCsM kQnpJqtKNFMSxaHoReIrEedNDxaq8yB2a8GQqWCOBHg0a52MWcO0FEnczug4n7MOKqlyy8qg8pPpONXaKiZxoGaYeZA1laz1R5ncSZZ0VaC0yGMkpeWe3FqWzmS1sgGepPDTlg3ZiYMaaI0tbLNJp+4XGuyKVJezF+AHN0StIn4gTkOD1QKRDP2kSL/QEc18dHLlwPTp54/M6XLgWCgKUg4C8KnuC7bvxYFxFeBUtjjBxhkFBc7ReeGLkGBS5I8i
y72aGWmVKlmxHE6xbsMjJblNzA8wo+7rm16HbQC6Q4mgiOIEnjiB5wXIeQqRHlGjwuak6YWUEnmUy6gM80GHPKPmlBqZzgYYOIseiLBoRDAn0C91KAuRfya85RZNLujdh4FgXMmCfJ5lqBz5EC4SIJUQW3yFZL85PO/I+GA8oYP8v+eZEAXALhloC8BZfKEF0zZ5MClMI0v1oyyelz848RgH6WClwS2XfJpAF/n/yJA4y3ZviEIBgLiAFC4SIvJx uRwvhnVzoZjgOuc3IwBS525XpPgV3MPm9yJA/ctQF3OpDDziAZ8seRotjkzlDwtitSWjI8WwzvFrcgyAln+oCJeuk4bucEvQChLB5VIQgJEuiX7MLp8ShqldMSW5E3F88zGYvPrnoyF5KM9vhlK3mZod5LoveShNzEbFD5x80+aPOYAXzJAV8nsqvyxEekO5LxKmc/JKGRFEgS2RMGJ2LA8Ri+DM72Binmxm4NC78vCMtwJQrAFsL9fpJxHrZMTv
YLE6ZSy8RKLIwUF4UFD8x5VguhKwl9Z4CfBXwJNmOj1JeytgKQvIUdLmAVCyQDQo2lvD/en8oZXtOwUfKtqB2bNr4u1aqYVFHrCZMsG0BkE0ktGIEXdDwSiL8Ev5Rid2COEVtDRkE7nOwt2DPloiziZJOovRGgDYJQ3bEZkSelpEk5AiYxanN+mYTKGt0uplnNCA2LkpdiidngOLmOKKx4MqsRmS4LuLOKpUhxJI16FMrOxkOZuUQRWKEJ8U+wQc Yb8mdKeSox1Dtp/I1BRLN5RSyuJ2VYAfLPwXSolZ15QSXOLFAULHyzUsSQgNoXaijZuophUD3PaLIBpiFPARRg9lLwuGHEO0WWhZxP1xFCYn2dj2kXs9ZFlydgdWjuFr0yue0qCZj0OnDL9mMcjRTdMnDnSjlIsytH5POUBTuixFebDcuXR3KSwiSsTK9N+Aly1AZc0eZXKBkpKvFqY9JTkD8WPEAluSoJWKEKXhKSlzi/6a6w6BVKKlE8pIDUuX
W9yiV9ytuu9DcfZRJKUqiZOUxvJF0uHpK5hTM7JXAvnlQxqZxK4IvMWLa+h5QVquoOuWxW0rr49KzYD2EaWA58ALSs+dMsgU2qQoos1ZVLL6UMEBlMKtEgCoOXoAjlIC05TGsuWgwblNMhZYMDuW4KEQiay0RspTUgIB+Oy7+fsrGWkAgFxyqZeco6XGtoF1yl1QDnzKILXlzy4meguQXqz3l9s3BV8qNk/LCFx/YhYCoQBkK21FM0FWoHBXJ5aF nEBJVTc6Vb4sNhE8eubeSAEqr7mkAB5qq0pWKpeFXTKlsc/VTEgRktKoVZQNGavJGKNK2lVAXGZ0oJndK0RqEu4AMoQAnyolwy0ZeMqo7xS4W38X+P/EAQFCvR2LPXvCCco+pYQlEJ9HfSLDqJmZ5VWlMEx2AzUaI2iStA8oxLScXeB3J5cVOhCQMgV/UCqTLO0i4LA+iskScrKgFkKiFVC3WTQq1FSTupm4jAZsPFqEJdWxGQaSfm4jdtKUVFQi
Fs4EveMnIw8mFB0xwhsGQS7UomUDWRc5NwRzBHoL5PFBYz2h4tQK+KFBLtGWDdBNgmwQOekz8LatRhlpVMEmFUGwVrpLKoVTflPTwS8R4UgxeiCMXRUTl2YlXnFPMXYShVacv6RKtwGAzpV9I5hpAVInMiMyX2JVWMVbr1K0wZ6UzswNkodjZm93J9dtF9yR4e56zBRqXKBzOtzVyPM9kkuJkpLJRDqjtVkr4w5LLmVM0GHaUfV7Rws1GN9dnmcC u+0EXdIUg3PJYDDSxXQSnhQHBhWGLWk+Sio+EOEMuggnkqQplKj8RICOhBZjYwdN6EoLvwqC1B0dAwrHSMGddE6eggRQYJ/Uj1IsWdV6DnXxiGxkptgsuvgG6zoAH1TAJ9W8DboVZX1JHdIXjHqzScQhjGTrHBogAIbSASGzsr0rimTLb5qudXJrh1xpSPx9lIgdSlUI+oCUqvDnOtjpmTUP5u2WMAdhaHTpqUw0pRKvDhB7A4aeTIikVKKawNSp
fq8E+GX9ZaQCQzBw1A4KNW0rzWKyE1T8pNestfkAx35F5NWDl2GUNqAFTaiZc2lbUXKO1YAQtR7OuWlq0F5anTZWv005B35ta4zbspGWZrC6FmltbmsXWxqBN6MBzW+Sc0hkHlw61BWUCHXayB1U0FhXFonXGz5pegxadaLcaPCfmpsmdVcBIXzrgVS6sFRCujZQq9mcMBGEjBRigsupkLHdHWWq5GdJ4ZsPSrxOclJhXZZQJEdfHiD/kJ4QSDrl 0sxvB8rwV3cFR04gzrOOgH8TFJUfQqB01XGdTfuR6tYf01Nlp8RhVssZjbMgQcRVCylBBAIvh6zo0VqhJUiGg2pNFulB6uLqtIJUm5381aIxrvLAISBgQd0YgDAH0yAB+EEADCIAZhMyAAOEEADiIIAF4QSzA5hUGh1lBCgiOp5m8yaCgNgWJOvoJTqGDtBxgzOiyDMG50EsnjIQtBspiwbh6QWjOqFr0yRbot8WpLRZhS0cwUNfg0Fj3Sw1ewcN
6qZVByjUUcm6bUxvxvV3S8c2OuHG5VK8TF8Gsxe/kFW1MUNtRVbgXMlUYbGGWG5TrO2ykVyNOvbOsZKkt4GdJpkRSIRJVu7UaVi08c7vdCUpGr/lrUpzjjPXFjT96vYSCpQWmljz9xJlBPiuGaCkBhQMG4ZP0h2xMrhkoyEKn5EKSLgEqcbAGDurtTN9lkrfc8iQGIA+jW8nfNKiAmMi4g8IWVfAO1XQBg6mAkOqftVWeSz9ccDVRfi1RUhtVQdO 6sLrPVuC1NaWtsWxLclsXr7zvZGQlBImu4RQEYC8BRAumtsmTlpujlXjivEvxrU0UuvJUssE4wF57e3DQ5W/K0TgljEKy48JctCGstxZzal5RxMwWqdO1n6btTVO05jCh19Ugdc9yakaaWpWmvWTpoNldSIVJsyhmn21nsKp16kmdMWChLjTb81SK8R+yEVv1qKziT/K5vMm4q/ZhXCMRWmwL4Qr1fm7pQFvQANAmAbAD8Az3/VSpFBndbiO+qy0
QWncdlGrrTytzoy2RAATRJoU06aJhfVqNhhivQ8oGjBNOTDqDcUwwURQ7xDmNyOwLkIImegxlxj0GgGmCcBvum4NHpc2jtpBoV48qodyAj6QKvV7Ib+VeE/6btqLn7aahIMnDVlO765SNOCEv/EfAu21yLUPPbalIRipsSLOD253mi3q7ezGNBrNqV9oUmDzkkswItKPNPrA7kdGkaMB0jIEWUqkgAfhBAAwiA1IGkgADhBAA4iCABeEFaQ9Jy+M aCQY34XLX+vhhMBANxWyuiBrK1gbzBhsB4qlJLp2C6tEQmnf8Hp35ajC3WyrP4OFh9a+6g2wesNpl2075djOomcvwmWkzKeQxEYmMQmJrbd+Cy+ooJ0OCcQ4w38zZZDVlgLQZSa5WdMkwpQqEOI86L1IS3MWzVxN92x5Z0Kk2VNKQFhKwrJpSryae132+UTgzXZKjSFAOn5aBWB0ENFhHU8HXpt6YGaU+Rm6hhOQThjxcB+RadN6nfyHpCKUCO0R
OgKsXyCpjIJkme1Ha3nR3TlMdSyNvR+GID46IWmyR8e/PNq7Se+FOqnRAGz1kDqkRekvRXur0tJa9AUKqk8n8raEWdQApfq1TUgJ8p9M+4vWXqr017edeW9dSYV+5CZRM4mKTOLtUYNbrojibNtesjyphaMTKxXcFBrZVdX9iwS0r7gmKd0ddLHaCedKqaDc2UFLH3a6KQkQaeEUGi3TFLIqraM5BY63Q7rQ2Ts0pvLA3ixqZHEzqx1wbAIRtwxD xF20YpUVj43ee5px6ebzaJOxYhzjm1xib1duKneJgELPq0tGYNzB5ijrZbOdvmAXdDB53J0kY0+tGCYNA04xRdlQfgoIQl2o5S6tW/Devp8Ht1ldvWwIf1vMQa6whWungXvqX6pDr56ErEVaRtJ2lHSFu/BbbA3TJB1gfOVYDsCOA/zaUAszSdCRFl5zluQCjCNohh6/ECUl67sZehQUh7nu6C3oeKKwWVTZZtNBTXxN+39qphKevtUDpBVtSJJV
A8EJHXsH4qmbh6glK8LBLRimKCjdWzNVJfwM+39zvteo5TCnu7pca41PGy4aZXQACyHx2yCyv+AnS8qCiJfHbEMivAI6q+SOsKijpx1o6udneoUVjp7146Cdg+rvoiQkPeZEUGVXvpToT7CHt1pAVAOIZTm84V9M/XWhvqapb72dO+nKkIah7bIrDBhk/X8v510LBdL2N7J9h+x1a79RsZxCggrYGr14PoQlc5I/Ldb0EUicIRPA66+E7oWCZhIL nXTeuLs4QyzZxScGXOopyl6CoX7E/hUVs3+deArOgyTeMWKM5DgGzeaXju9kt68Vx6jAqerqEZ4WkCijhUouxX97ZddOhnagCPlMBXpoW42BQAhBXhUtaGtgz9Ey3j6OdU4LndPry167Oc/OlGIdSX3C6V9FWsXf4s7lKht9mWaXTwNkO66FDKYZQ/FGjRqHSAGhrrb4MP0Zs1dwQgeufvw2uH5Dih0gJ4dUPqHptpG2/T9VvkKNawSjFRi/ppm8
3SaMGNF0c1lbHPZW4jOVcvQxQgeW2xTkD62sA5tvQN0NaRQM7AxlPd1gzy58qnpmUOQLCyPctK33Du0oMgRqDKMleDZ3rniimaw497dEoT06i8ZXB1JDHy9ZaCflghiAF9iYBsBCIhADHVIZyD16wwshpvYju/Ct6VD7etQ430WTxUzjve/vchCJ0YQQE5hIw7hEyoERTDbhtYxsa2M7GKUdhxnQ4fn6NVyEzh2ohzs+PrHXgPx9Q0/V8M3iN1JO Bs1jORJpWNpQf5dehwdROw1C6v0PKs1elkeC21bc2oDI0TeiUD3wHWJoe4Ua2s5oqdny0e6qQrLj2A7nufytTdgYIPULQVY60g9JP+6yTmFg8ZQANRoPS0F19Bn/Ruk7ZcNxDzB4Lp8DUTepKxhUpvW5t9lGz/ZoIiNih1K67SKdXA/vaDGIB9U96pAYgKgHFgGAJy7mEOlodH26H1BX6nLUYdn0Fb595hkraYJF02HB4O9e4wfRq1OH8NNxu4/v
/PIXmLyl5b9D7GyU7DiACK1F8oHYHWQLq4pwh6+KYHMHCyddSOiwdcg9DkUUIQDiYg3TS0gPgbnpZRpbXypJHpzqjlijk6hvqOpS6Rru7DYjVaPHb2jGnCFnND1SXbc0owpFh1zZ6BKw9tUjrOvG2rFtY9Mw5jS0Z3psaftBabgx60B3p67VLU1Y5TioB1619vAQ4xX2b3V8lotfeCJUDEAXHDomhm49oYH0PGdkICc06PsaJvGjkHxjSOafp2r7 SeMvH99qGzujVmP3q7QjHWcITwLhOQnETWQeI8TMN03y4WxzU5uc0uYZH7KlFWkbfzPzbYdgdErFFmtxb7Zn+CTVQppL41zZVCeEYit5KkL9I7tTRq8ogY5ZOIymby/3p9p6MTift93ZTZEn+2NTU9tVOYa1OXGZ6SD2esg4wooNp9lArCtU0pIWPwr8i5izYJNVrVnDbeaK5nP0jnTV8+DyipadI1WGeSvNRUfbNxDUoUq+9I2xrfphMyAA+EEA
aqbyIE6ztyMChwTIZqnD4aP5n6/egu7grwX4KCFQj6J30Tum6DVdbYkjCRfsF644oLgIc4k/h3ApgU8V/oZTER1G05GJtQGqbZxxPzcdmTXK1kzYazGidTFnJ23Rtvt2+7yhfJ+xURIO0kSPdsW03laFyFRkujV88Yoz1mC2kSzipluUMZ7ErFVgubboYat4HGr497BxPXEuT2pJDTSx84SaaVqrHowM+apE7GoBOEiOS+3Y9eCtNl9G9tp445Mk BcIIAAYQEzIABkQdrZ1p0MvqWdnx1Qezp+OT6tBQJ39boN53VzCtQGxfaVpIKgmINlQKrd41oaS6YN+GhrSFqDNhnIzMZqbchoCNoa0TbzIIdhsxO1VsTlQas01pDMRnozsZwkwbpdOkrdpC2w2J6x9Z+sA2tGjNXvyWCI1NQFYMsAeEpSTAyJjbY5Uutt00ta1lR/cM7s4ja91m+4MTX3SD22pHtLRhxBKZ94oG3tcm7o18sIUKm/tuBlU/gbT2
dMzIJALp8HZcdird6PTfenQ96eH0z5/TE7QM330qB3nzQmAB870GfMJBXzfx6fgCfX1RnN9bOsE64az33mqkj5tCxhYdFJnIVAu84pcWuK3FaxVA6cc4JciPlf1crNYLRgsa8K/BeKHeRHP3miLbOEQ+UwjIjz4JrSNJmM4FIKPaKijI3PRbAfQCLbuzcGK3X2c+lcmwauE4c/nILqYGBTwMoUypzaMuKemUs6ud0beiDa0jRNKjRRvTDkbhjzvN EHNT7U7U8sMNnunjZk63qeex5DzHzNcUYqC4gPSOz4e7DO0avEr3XaSV5QBaaOZxVSLCd4Y4rpGwkPw6pD0E/vYCgRZIsUWXATQ4mbZ16HUzBhqfRmZn1Zm59qdBi/mZBPWHizEgegKReRYPHUW0J+wZUBIv2gyLfFii/4YP0tmAhbZk/Y1kvN4bh6wlxFrxcejiWUhrXYk3ftvngdIOF0Kk5mrvHJBZ0QTWUmJxs3Mn08yQABYrSgU8Ht0kOBML
yZalxMamPuswuoWar1PxK9sAOy83uOvMg6IT3x7Y5YbnWkBI1MAUaMdgoB/Bhwlp0vjafkMt6lDAEM44Bd+MxV3TTp3HeBa9OoQ9DZhNNWTrgvBnKgkJzY2FdQARWorMVn+vFbDP2GcLdeBfnhZjMr8QrUJ6q7VdwDRXIdcV0gAlYPKUW/DCJyoPzWzCC1haWZmDVCy61utcEFB7sA5BD3HVcz7C86gkyureSA6LCbNh1w7C9hgh7khIo2bpMDdp SLYhUCmkl+T/cKck1inYGE7KdjOyj3vLnzimhPcQpU3J7Pz6m78yMaINgrx1kOkCwXvPZ8Us+86s05Ag9mczyw5rF9hjs3XCcgocYXHeIwwsCHsLa0vRmcfJ1hyML/evqmoBCCoBQYfQNMDph5hFsFdTO941RYy3JmaL3sb9X8aYsAmWL/mYE8vtixgmNMHAYtg/K31S78NtVvIQ1aaucAWrbVkw2VkkuonpLaHdswNs7MTZuzEgBa/VcavQQVrq
tqQ2bbopgMsn4DbJvIb2ZW39nyRg5nk1tpHNycxzWBiAHy1lUmWF2GZJkBZcXNWXOLOpCeI5ZbksJlTFqu6tq3CVvaWDH273sedmNJ74i27NPSaKCuZ7Kg/4YgC5SWqkBiAqAPiAYAhbQ6PzyVyvqlZr7KHcrqhoC26dAtM2f5np+44VeJ3zVFqy1L4eTveMT7CbxN5auTcptNXsLjwxwyCfwsgo4zBNomyTbJsU2sgiZl4VRf8MCYl6K9NehvVm AVq1Nfav66b9mlpI3C2S6pd0umXfSy/MEZxAYwKF3+tWkLUHxjEOyw3iJ0nTidyqpYVcnhTNxcQeIAei8yKbAZeX2JGC/y9KYwOx65T8eqVu+dU2Dq3zwKqK7+eIN0KVh+m1VvFeh3UNmgcKhzngLLVHBTeZrXSS8CYPX4crWx6dGnlYivB9j+OrC0caJ24WKr+F+fr3o2IyGddDOofR8eovfG+rvxhi8YezNmGRrguyw4WY4t50I89HUtg4bmvD
vODVMcQVYHzy4lmwVd7/ANdxIQY7XldvuCIUtYG3+gYKR1MbVJc0UyXvYD07VCbqgFm7Xpkhns7UdqYoHEp6lz67pfW6NG/rOB7U/UMBtNDNUvZc7VKYD25oqT8rOI9qodSDC7uK8IjqGvTAIIJjkSqYyaq8u6nODySPbNjd4MzSM9aVhCznuqQNJAAfCCAAuEEAAMIA0kAAyIAvvIv4hpDDet8ylftPx9Gb/55m1lcpo5XJ7HN/K1zYsPD64Ul5 0Ij1121Erqkuq70TIR+S4dep3i2DbYavpROcqBfCtgPwv4YGsfnvDiRKQDCKN0JZTBIaS5IsGGicvP1iKqhSxS0NnTUoID0CYsqWB5EDtLzbLJ7WHFeUPnOjAVmUy+flNKasbYV1WS9yGORWR1ox/WQBYh1AXIV+p6hjQgguuL8iOwI8F6hoinCLxhwu07Oi2zFQ91t6kq3zZwugSAG0YnaYor9Oi3h6x13AKgGkF8xVAbIK5t1eH0CNpbn62W2m
CcFbWMPj7d9zdqpG3a7u93+7ktq05GdavAnvkctv/ArYkB76d7Hd7u33eP3PDEOx/TaYLo0baNdG+jA244WJqAiMjbvX3FsHHj/jV4WK629td9rK64m+wDrn4mtRdgUwGwSS02f10tmXq3tjs6UYesqXYNz1yo69YSl26PrdR7607ocUTmnFcq0yxpxEzEG8aFcRxFsBwQBLQ9MN3OxHqGA7Als+q/c8wclHTH0bI03UUkixtJgcbyxkcasYkxf0 e52DWANuZhfULrVvjXOLOQuq/kJ1uVnR7+9iexdcbI1g/gs9xXc2a2sm2ZLGJ82xfsqBj3T7U9i+6QCvvC8RzGIrS3C19H+jAxwY16wsqCiM4MILUCtf5SKioX+O9QsNBzOXRrxrkzIqJN6h2XaJWIUCJYA0VgOE1PLx3Ypm0eRs4LUbX29G30fFYDGcbWdvG4XeitjGdTExvPVMehXi06gVNqUhmHyv7hio8YavZ7PYNCK0I1aF/hst4NFXsV3d
f6f9LgIlZkPl9R7ihhm+lfZuZWYT2Vtm3PduMQXubjxyoPQGke/1Sb/9UqyYYn1SOTQMjkx3I+X1YWj7c/E+9GeX6X30Alj7+sY9PC2PMep+zW+NYkAHMjmJzM5l/alJ3QYWLkZ2zz2Z7REYGoDraxIVtt/9qsKRyeLdCWYZHPJ2RoAe7fyMMnwBcljB4nK7OB3VLcGvBxpYHM1GhzudL6zgP0vR3/ruB5xUDZ6bDXPrC5uNZ4t2qAOsE3YqZrDf oC8ceJ0JMoxIcslZce9n97cw34VvpRbDrdWP1E+ui+meVuMWgsitje6xa3tYwizGtiQDiKgB4iBLzhyoKo9xEKqJLKJlXRhrqwP3cNFtiAA4+sdOP1L6I64iSe4Qchvxv4/8c5znPrbAm+4P4qoRuVHhi8ft1CBsBkQtiIDsYXbnDXpZg2w0ENkRzdphtx24bR3XO0gc4kp2qaJDkAbKeKWZ3gripqh3gYivGnNNGev80TcAsk3gLhm8m+e2ELJX
bk0bfcJJaeK9oPNl2jzpqyu8I5ruiO67QOvG43YkCVXoTvSPYzTcUd02x704tveo+Auz2EIOjgq0vaePeZGFrx8xwn1WdhXD7EZxx9ITatOHz7sZwixVdCvT3p1yZ8HoLstbWtbWhAPpmibmvMgHIT5Y9pwIJrv6x4ERrBJiw7o4s92yTjh4+W1Z4JAxtpLJ2dZyfIPQD7sEDUyZKPFOsHpTnB8HbAOh3CH2l2p5HYaOYbBTh23DfgYzL6BaHLdX aDixuKHbHPUm9q9vmwR5uqmCTUT+Sxbm/wcOPSP+bfdtgQo4uNVXpDetq2yYbeMJnNH8Z7R/oc6CGH5b/x9e4Cf0dsWxr4Gix4WLsMvF8R5MI+9rrl0S2mzm11x8EY7OP3wj2z4c7dYTXka4W9kxyc5NcnAOJC5GPYLSOcRbr+ceEPYzUJ6SDIVgc3aiZJyW50tIcOKebO/lt3P8wFHlr/qKYIfdCkbUpqp58qCuY2cD2Npp/nZafp7tNSwgWsTd
NIw4mGysJKnG7O2IzCVLY9gPDyYyjf4fTPRpVdkRxec0FXnjZqx8w4+K8PYPqbSVrZ3aeUcOmJ7CEfZ6zeuPs3jni9offoeBYptLnm9z4wq88PWHSXDyBnQ4+Z24XnnHV1xxAEtdTklXpLr534/P17Nn2r7d9p+1Cd/Co8yCHNkqQ3IR5BhX4fBHA2tvlt+eVbURU1sOtLNjhySWIZQjdu4v6TqDuOiFJ463WMh9183Y9aDs1PB2BD969S45b1Op z2k2en+otPvguL0pXqb+RY8ArWf7jOm7G6r6AeCMk8RCrDrYq4s66cyP1pdQtQkFEqsC9/Nw9GoOsm9oKBvaXrBQL9BpDhB9MXg32uLFrC1g4zHVvZ+loOcpnl7uj1e4Y+YtFaTHqtsx+rYSyYS5Ajzxw4JYkCavtXur/V4a50x6YTXTx815a/ZRG3b7bj3a6fv2sKWIhAbnV3q4NfBBQ34bs1xa/+caXAXRu3R5UAAA6alq1/PYrCL2dHxz+i/o
VLuwy4y6nN4aem3j+c/WJrkeKPcbhNMLRjsth6BjYja1P+V2gph3LrBtG+K6EfiEpXYj2VysYT7fAE2lmt8/setNqufzpxtRx3oOdaOjnnN5Kno59POnX4ZkzDsnbH1C2F3J75d5hbtf3OHXTj9qy47ecSBF3pkm9xRY1tjWfXLo7zr5x3BBubJ0RD2dEVza+5FgYeDFRR32vEJRKUN67YJfujIr5NsIpbBsGxdNVcnk2sAzfnzdFOFtJTy3eU6Q +CCUh8FCMYx/o+5hV2C2n8OoAcE/gAhOwNmUtzZMt2QuDCNIyKnboipsNCj/SGF+1GmluXdaWL0tbiy0TbcFoiYLJiWEuV8PiX8N0l2HoTCPp0oFL9A9U4zsY33w2ADQNB2zsan2aqp0zq0+ZdZ62FZmmuwVHnSbuNj1UXbWip/1wvbg+K9vXNDwviOZX2K+hey8wv0DD14H7vcFMF7QTy7HCm22kO3s3OWHYRX13Y4kAluPQXj7D1872vx2Y3nz
P4Oam1Tohzpfwl6W63vkpp3HbwNxqCDuANgGy/04cv14vsxGf0O/Jw2SYRZQDjwN4f8Ty7LG7y5K7mfSvjR4j97asZcpqAQgVhrcCGAqSsR4QU5dZ++dVdfmlHJxtK3s+3e6uW++r/dx30Pfvy0OGHC5xe6DMT65PV/RT9BE4Aqe1P0921+GaZ0n9+Izj7fbZ/k+4AHPyn1AKp/Q6fO+d8Jn94LokHzApBMgi03CtEFSlaMxtwMb6GiI9vHEltpI 6Y8UiEC9Zf73CLXDUHiBesEAv0LYEYDGCYAOQDQZwPEAdIcV8A4sDil62FKu2/Gp9TNQfxWBWmEgDJwRusF14Vrtzh4HFEcBDQe7FUjE3B0YiO0UDOTSr9/E0mKdoLx2k7adrOwqeSjoQi7Zwog17XNPc7jT8Kwy7vdMvQdLLpVp0/A8If4dlB9AJoCOB7i3hpotSTTa25KkMUFwxm1cNWzXihFknbbHsBc0SP91crqyfMjNIDusRwITZH1TgBbB
PDNq4s9QJdtuIIdYmHW8+jmb863rrxd0JUKhTolwR5JdEfcHJHyp29fI/Vvtt1HvbbR9jvCmy5opqhxw3WQp2BKad0gw71tLOIgk1UthzQed5Kk8VDkQYRErHq9ypnFdiV8I/bFK6FnxpuVwnzs8Ke8+7EVQDSAGDyPh7DcOQ9s41fj3VHc9nV13r1faOTPDMSCyAkv4hABbZV3z/Z4O9mYswLwE73Y7vceeZbZ951y+/QB7f/PP3o7/9/VtP3vn MAIYsfldRkUAeZnlKJ0ULZ71wfb1YUuCQ2QQkZikJMUhI3ddeFwtUvSQdL5l9UaROB3G2yIpthqPv50VFbzZTclOwTVzsPt1FBqVAOOXuG7NzUrO4aOw38HpTvT75cM/trUD72nKunepd6dKHKsm91+cZc/mVxjnsDxuM5dyTCY52Dh5D0XiyZpMCQdHU7KuHWnWb/tkyXtlQu0UouCzgnT3bKuzQCv/SLvcPdjrP2T7p15qxdbWuS2urtr3q4xj
gLJ4KsPWFbDl28XpS2Ec6hbzVTsDjwr4SjdjxEggIlhJqSCHWlXayLtANamxWHWia18bsHEKAPC8LryQtKDNul4KXi3Ad+r+S4rdkfuTrXupyWJ+sGWmjbu7r/Hd6+tONOmw1j/RLDDDBdgswQIfbyRd8uZKIlFhPKDs4l2lvTGmUXHbE/rfHuwUI07jZ2+fHowLQAfhp9XefmR7l33Tyo/0+un7vRnx7wvYPenPKgNgqAHYLMfmus91viyYD/c+ BgDWnXQ1l15c9MffNzHCWKjzR7o8MemPLHtjxx6488e+Ps155zwJfvg/zrl13OsiZ61BHTb3zzx0/aOtg/lrukSH1dZMNIeuBMHjekC+4TxBvaFAL1s0BgC/QEAfVHmLmHFhet4gnYDdL9C1zAgagBQpXvZXWAX1smP+7WkV6RfPtTsHEWMGGg4YTUsnGNSd5F+fpzQw0c3gdrGBkQpBZM3MxJmIq3clOIkZhHywZ+Icmel25n3o7e8gEfnc7FDu
AnH3Tr59xPqt+2CQ/PjuE8bS1sgJlRqo9UZqMA85m6BswCIWeh67wyvCFNZyAGqwLhO4RDkWJ5T9snsK0n6R3FlkaQcs+tFKWar77f0VwGS3yrtSy9aa+VuWvaByj47vQ3O7OvzRiXwx5O0cNfMg3jt8qu5E9A/t8LAURqvYeh4aynCommO9RueXRPMz6dxt9N8BX0e87rq1VensquFH2nx37+a1fOmDPbv7HcZ89+mfvfEgQZStQD+XuT/azxiP z4d61MdPS7XT1z6BZtSeeVvT77Pszwh6kZIEGTY8NzNQs2i3+H7/hnShuB7dgvwHr77K5+9OrEvO/IpbfMWA2YeYnYUgB+AdLb4gJHp/L2VC9STVVX1uUr6Y1w4Vfk2VXojum3Q3ISiT+boJ4bBr91+G/TfiF519jDscSJyJDFCcBgfXBtgp2PYL/RBLHg5mE35YFN5ctNt3LKniTW790/eX9Pflw912tIc1OCFdTml78t29YNLP73DU0d8fesvn
8fteefhQp9kMAvOnVhpA3OoXr47fuKZgJjzii4suI8Aq4sC7OCziHAz+wQriCLKkBPtdBLMv5BGIgSv/G7IZ0XWkmBkGSLFkYxmWbo36e26RC36FuCcrV4d+pLoUQNeENCHaaWWdIL60u/Jo05dexllL6J2U0DBqSmQ3p25XwVpKJTJg9vDwbq+vYoIq00zCB6yLexsmK6reU7r9r7+s7oFYW+GkMSBkCPFAoA8UmjAoAvghIOEDVI+/O5R8Q2YN Pp3/Pb07j/bYV3qn5y0MCFAj9IYXs3aFQqwGir/EC5MXid2z4mX7yuyzv94+6+EHDS8+SjtVbH2i1pT7s+1Pgba7O5bkmaHOtFtW56OUMArbOueZmj7RYGPobCC+wvqL7i+kvtL6y+8vmsCK+yvrY7zWrPmdb4BUPu84uOR+vfZm2TPrwG4BbPqtac+ubgE55sFHobCAo+AIChsAzQBxTrIcAFrh9UzgDAD6An8FACLAH4AcCAosBGkQ+eTHIJ4L
mAD25/md5wYF3uq5O+mrjd7aud/hoa7ulQAa5e+RrpUCjc7oh95XOnxjoF6BBgUYEmBFSFUjmB5NlYED2bns1bS2jrrLZg+E+sEH6BhgcYHBAEQVEGWB1gXD7Xiifv47oA4kpJLSSskhn736ZUkVxGcHYIEga6+KEWwR4OAcBK5e+AT1oB0VSo3KEYJ7FggPQ5AWV684Htvk4oShLq36KWeFHV6IGLARS5sBP0hR40uVHlHb0uDbpOYimnujOaao m/MgVbxggyGjTwgZOpsqrAywOdhCMcTvhAoo9/F/SF431v/TcQtauJp8iV5k2o3mvKEQ43+YcNKL++5DoH6TCdLjZ642wxnQ4E2MVuMYTqZ3iR4ee22EaamccOrsK+etSvkSX4u4MYjOUytItB2iqKMCReoXNsX5PiDFPF4eicLLWAOkuYD+DrIxAPqC/QbfFBC5cslHYrASAHspSC2MYkPYi2cbNVwpi8EoRzAsxHCiL7C3PvV6ws3CC0FtBHQV
QgHL6eK6YN0DKYfQtnadQU3k5ZDA0wA5CS0SNhM6iuInob67+agY9wF0ZvtJ4o28rh4buuxIEwBvip3gcbruChk4HXeLvizb3+Whk/7PeZniAhbqj4s+IfB09oLY2eZhq8FMAqAO8Gvirnr/73u//k85JBUfvCEiGbwdCEaOvyqNbheUAYiQ9SfUgNJT+jFhLqdQjQZTzryywHWSLAbtEsziKyAR5JZGvoEm6iQKbgaLpuOCKV44ulAUMGAgeHjV 0Fz+rHEeCnY/lFK4bmTQmxoq88YAb5uBfkuWD8K9luVScq+RsHLe6wNjg78Q83uf7PKYcOEFGe2Cke5UuWBokH9GL/qqJv+6piDqjqxdt/5R+LnlDpcuyFJ57jixpnDp0GUFpsAIuS0Ourw8SDnaLI0heMVBOmsXl3bxebek8yAeIwd37quEQrmAlKVHtD77OVrqQH2u5AY64G2jbhc7p0BZu6472tzhABKBKgWoEaBWgToF6BBgUYEmBZgeh662
7+20GpME50fPhYpaW/fvMGD+DTksFi+RlkdprBXFBpwMQ0/pZb40D0OqzBE9vJt7SBKxN0ITChGJv7KBO/mt7jS8ulNKH+tqloGVAb7mZK2+mzpf6OB1/i4G3+rvu4EPee7iCF+sL3se5LuJVhvaf+GkE6Efu1wGiHA+iQaD7YhnxhGEwaXrpAE/OqxhZQuhpfHDoOBG7np5nGDfIZ4P+c9msi4gAmDDCbCyQDDBvAFYB0ituHIhj7ZmFQUTTIIf pIeSEq+QgXT7oa+Hgm4/Ow9GSGYAFIdfp5u2Krz522EgL9DYAiwEIBGAfVLAQ1AXrD+AkWDQJ2BjAmgJgBRwsxqr54SmRpxCOU8THtwUCsnrWpEQMPLSLwOOKDDyCMFQfO4Uo3gT/SIkfgYAyn+QQQnahBrwaKJtq5Umt5mEUQbKIB++3lZ5/BwkgCE6y8AvQ4ghTnmCG/+aHtOqVAnnqhTV2qYpMx58nnMgp7gguGNKsQiFoQKhck8vcLOmkjo0
iHsDWkxSmHLAOowqG5eEPoF7TuS1bAHSiQaTp1y3QWCIWY9gNJhIHleObjh5ekeIPdBMINoKMFc+EoRUYG02ABoD+cpHsL5VucoTW47hQ/mQ4dO7btqEUcWrEjJw2yAbgh4Iu0BwZJIlHAtib+dHhL4Whcdo8FzuI4uP6yEyYZKK6GPNsTKwh8FrlSzQLrumEg+QAVJbRh4fl7ocMpeDpDgA2MNcBwAcAKSCXwDJNADHAathIDNgQ1m0AMA2xhQA Fvi/blX5wsygB+CAoHIDwDBAzfjl74hnPC/xuWq2JgEbOoUn37wilXjMGNcI/rV5j+P9vdbcIHYV2E9hCAAn6thmRkFCqE1KLDxhomEIziXmroeHYzUBfl6Fnky3NcFaIGwHcFicDwfLBPBjaggY7urRpGHtGHak+abe3wTQ7P+ypiH63u7/kCFF2YOiXY56OYV6p5hF3skKJ+fLpw6Y4S2ItA8QPoR+68g9mpM5bGRvqrwXqCAQ0FIB4Hgq7G4Q
8UnPnOGdARYMJw6CMJAZrNA+gKSAccNLAuHygeEX5yEUKoNRFERd1p2YTBfQMrSURHmtREZBzAZGQ8RLEdRG0R8UgHjCRVEZkBiRWuEQ6SRfEZkCsQbXtxHMRUkfoCpoNHgqDyRUAKxGZAL4NmG/B4kNpG6R+gPpEbOmYSpEiAIkZkDqQbevmFFAxkaJF9qMWsByORmQBWCayLkaOpxeFEdZF9SIPIRB2ER8ExFWRakS+BDQSkeSCaYTwNgCvARI 4XsAjhwPpoL2O4oZSE2u1IXa7w+Jzvo5UByPjQFuu6Ph66GwmodqG6h+oYaHGhpoeaGWhcxhWY768oeRGShgRtKEM+BHuIFCRioRKEka84dBJqh/PobDiwawDUA8APMBMCEA/EeYGEilgSA4IKyQGVBPobHNeEuhbGDIS7AT9P8RQkinoFCSep/mhAyIlmp6GUiVvLNRhhCNsKJ/8r2qnYo2x7lt4mcQfvEF/hiYQBFtOhNuCpl2EIed5ZBl7EWH
G4pigVXDgiSML6taTTADvITIxRcUfgCKqYoHAwLY8xLgjS0QIoM5lAasAYAN0DAAQC44rsNiqjCuwDwCk4bkfoBKRuqIAS1OCIHhGwgJAHb5aR3UcQCkgCAGyRGR/UWQKjKHkbgAQI2gjHYkAQuEAg8UPwCAikAygJCBVIZsP2CSUfABQgHA/OICID2h3iaBYg2sCtG4Aa0SQityT0NtFPmDmj0hNRqkXsZ0IGkY558IrkfBjsQFoAchrKQCNkCT 5B7nKWGQIRkuWD3eEAY94aSWfi947g8IA/R/udQc3rNhCXJTwIAsBOshCAmAB24u22XO8KLgXfKBxLIKyGsibIPQaTwd8eXAMGt+BIUEwTUqKFgGqhocv46oS6oegAlRZURVEHAVUe+LzmekZsDzYz/JtLnqmwA2Jg0WNIeAa+Z+G5SDILQlMB/y2iIeA8yvus4hEuj4c0YeRckPyj/87wWga3+fkV+H1OV7mKzBRqYfe4OeX/lmGgR5BhwrueVQ
RwQIZDH2frEQArSLeEchYRCQaojMQg4DGGPA+gFiAfApANGD6I0EXspwxTABNFTR/0XBy6EhdJoAiYqKMwDEgRyHABjRp4OjF/RDnIQbQQjAIRBsAPwJVEZ4YQMEBKex0K8x1ogUbOj12SzsmQGA7wUzHPRI4pGrPA/4Ep7UxtMZAhn8j9BACOAzAL9EewXOouBkC2QJsHqSUojpodIgQHiBMA2QCvAYARyBjF4RWYImpkxb0ADFUEC6ukqExNmI AcAROAzqab8uBUNxAciNwOGhjS+3CzZiursJfhru9Ydxg4hiAbzZLOvdndRksvUWOG3q/emohxAsQPd426OHsz7oA2MU0hxA0iGxAURI+pW5HO3AvSEmGjIcNbMh7FmyEJYykapHqRmkTwHD0xMbjFkxeELT6iRrZjtayWMsJeaLBGFgpEFut8lADLIqyBshbI/HgGqbhPEHNjeSRkfWJbSB2u9YuIZLNF4ocZUMtx4QScsUFSui5jXqn+2wcUSs
mx6wxsZjEPuzGJgC8xqKCGAkx5kH+Foww8LBrhAHMJjDaQQAA=== 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
``` ```
%% %%
View File
+28 -13
View File
@@ -1,6 +1,6 @@
--- ---
generated: 2026-06-05 generated: 2026-06-16
total: 505 total: 520
--- ---
# Vault Index # Vault Index
@@ -9,23 +9,17 @@ total: 505
| Параметр | Значение | | Параметр | Значение |
|----------|----------| |----------|----------|
| Всего заметок | 505 | | Всего заметок | 520 |
## . ## .
| Заметка | Aliases | Status | Type | Tags | | Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[AGENTS]] | Агент — контекст и инструкции для этого хранилища | — | — | [] | | [[AGENTS]] | Агент — контекст и инструкции для этого хранилища | stable | guide | [agent, vault] |
| [[CLAUDE]] | CLAUDE | — | — | [] | | [[CLAUDE]] | CLAUDE | — | — | [] |
| [[CODEX]] | CODEX | — | — | [] | | [[CODEX]] | CODEX | — | — | [] |
| [[GEMINI]] | GEMINI | — | — | [] | | [[GEMINI]] | GEMINI | — | — | [] |
## 00 Inbox
| Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------|
| [[Вопросы по Вебу]] | — | processing | concept | [] |
## 01 Ideas ## 01 Ideas
| Заметка | Aliases | Status | Type | Tags | | Заметка | Aliases | Status | Type | Tags |
@@ -45,6 +39,7 @@ total: 505
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[Анализ рынка систем учета посещаемости]] | Анализ рынка систем учета посещаемости | seed | concept | [] | | [[Анализ рынка систем учета посещаемости]] | Анализ рынка систем учета посещаемости | seed | concept | [] |
| [[НИР]] | НИР | seed | concept | [] | | [[НИР]] | НИР | seed | concept | [] |
| [[УИР_Сводный_вариант]] | — | stable | document | [уир, мифи, разработка] |
## 02 Projects/Наука/Статьи ## 02 Projects/Наука/Статьи
@@ -210,6 +205,7 @@ total: 505
| Заметка | Aliases | Status | Type | Tags | | Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[CI-CD — основы]] | Основы CI/CD, Непрерывная интеграция и доставка | processing | concept | [devops, cicd, gitlab-ci, jenkins] |
| [[DevOps - основы]] | DevOps - основы | stable | concept | [devops] | | [[DevOps - основы]] | DevOps - основы | stable | concept | [devops] |
| [[Git]] | Git | seed | concept | [devops, git, vcs] | | [[Git]] | Git | seed | concept | [devops, git, vcs] |
| [[Gitlab]] | Gitlab | seed | concept | [devops, git, cicd, gitlab] | | [[Gitlab]] | Gitlab | seed | concept | [devops, git, cicd, gitlab] |
@@ -325,6 +321,7 @@ total: 505
| Заметка | Aliases | Status | Type | Tags | | Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[Infrastructure — ada-dev]] | Infrastructure — ada-dev | seed | concept | [homelab, infrastructure, devops] | | [[Infrastructure — ada-dev]] | Infrastructure — ada-dev | seed | concept | [homelab, infrastructure, devops] |
| [[Proxmox VE — local, local-lvm, LVM]] | — | seed | guide | [homelab, proxmox, lvm, storage] |
| [[Проблемы дистрибутивов]] | Проблемы дистрибутивов | seed | concept | [homelab] | | [[Проблемы дистрибутивов]] | Проблемы дистрибутивов | seed | concept | [homelab] |
## 90 Library/HomeLab/Proxy ## 90 Library/HomeLab/Proxy
@@ -344,6 +341,7 @@ total: 505
| [[File Permissions]] | File Permissions | stable | concept | [os, linux] | | [[File Permissions]] | File Permissions | stable | concept | [os, linux] |
| [[Linux - MOC]] | Linux - MOC | stable | moc | [admin, os, linux] | | [[Linux - MOC]] | Linux - MOC | stable | moc | [admin, os, linux] |
| [[Redirects]] | Redirects | stable | concept | [os, linux] | | [[Redirects]] | Redirects | stable | concept | [os, linux] |
| [[Sudoers]] | sudoers, Настройка sudo | seed | guide | [linux, sudo, security] |
| [[Super-user]] | Super-user | stable | concept | [os, linux] | | [[Super-user]] | Super-user | stable | concept | [os, linux] |
## 90 Library/Machine Learning ## 90 Library/Machine Learning
@@ -408,7 +406,7 @@ total: 505
| Заметка | Aliases | Status | Type | Tags | | Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[Дискриминантный анализ - MOC]] | Дискриминантный анализ - MOC | stable | moc | [math, statistics, discriminant-analysis] | | [[Дискриминантный анализ - MOC]] | Дискриминантный анализ - MOC | stable | moc | [math, statistics, discriminant-analysis] |
| [[Параметрический дискриминантный анализ в случае нормального закона распределения классов]] | Параметрический Дискриминантный Анализ В Случае Нормального Закона Распределения | seed | concept | [math, statistics, discriminant-analysis, ml-algorithms] | | [[Параметрический дискриминантный анализ в случае нормального закона распределения классов]] | Параметрический Дискриминантный Анализ В Случае Нормального Закона Распределения Классов | seed | concept | [math, statistics, discriminant-analysis, ml-algorithms] |
| [[Построение оптимальной процедуры классификации]] | Построение Оптимальной Процедуры Классификации | seed | concept | [math, mephi, statistics, discriminant-analysis] | | [[Построение оптимальной процедуры классификации]] | Построение Оптимальной Процедуры Классификации | seed | concept | [math, mephi, statistics, discriminant-analysis] |
| [[Принципы дискриминантного анализа]] | Принципы Дискриминантного Анализа | seed | concept | [math, mephi, statistics, discriminant-analysis] | | [[Принципы дискриминантного анализа]] | Принципы Дискриминантного Анализа | seed | concept | [math, mephi, statistics, discriminant-analysis] |
| [[Условие отнесения объекта к классу в случае двух признаков]] | Условие Отнесения Объекта К Классу В Случае Двух Признаков | stable | concept | [math, ml, mephi, statistics, discriminant-analysis] | | [[Условие отнесения объекта к классу в случае двух признаков]] | Условие Отнесения Объекта К Классу В Случае Двух Признаков | stable | concept | [math, ml, mephi, statistics, discriminant-analysis] |
@@ -452,7 +450,7 @@ total: 505
| [[ФА - модель, цель, сущность]] | ФА - Модель, Цель, Сущность | stable | concept | [math, ml, mephi, statistics, factor-analysis] | | [[ФА - модель, цель, сущность]] | ФА - Модель, Цель, Сущность | stable | concept | [math, ml, mephi, statistics, factor-analysis] |
| [[Факторное отображение. Факторная структура]] | Факторное Отображение. Факторная Структура | stable | concept | [math, mephi, statistics, factor-analysis] | | [[Факторное отображение. Факторная структура]] | Факторное Отображение. Факторная Структура | stable | concept | [math, mephi, statistics, factor-analysis] |
| [[Факторный анализ]] | Факторный Анализ | stable | concept | [math, ml, mephi, statistics, factor-analysis] | | [[Факторный анализ]] | Факторный Анализ | stable | concept | [math, ml, mephi, statistics, factor-analysis] |
| [[Факторный анализ. Цели и сущность. Постановка задачи. Отличие от анализа главных компонент]] | Факторный Анализ. Цели И Сущность. Постановка Задачи. Отличие От Анализа Главных | seed | concept | [math, mephi, statistics, factor-analysis] | | [[Факторный анализ. Цели и сущность. Постановка задачи. Отличие от анализа главных компонент]] | Факторный Анализ. Цели И Сущность. Постановка Задачи. Отличие От Анализа Главных Компонент | seed | concept | [math, mephi, statistics, factor-analysis] |
| [[Фундаментальная теорема факторного анализа]] | Фундаментальная Теорема Факторного Анализа | stable | concept | [math, mephi, statistics, factor-analysis] | | [[Фундаментальная теорема факторного анализа]] | Фундаментальная Теорема Факторного Анализа | stable | concept | [math, mephi, statistics, factor-analysis] |
| [[Этапы ФА. Выделение факторов]] | Этапы ФА. Выделение Факторов | seed | concept | [math, mephi, statistics, factor-analysis] | | [[Этапы ФА. Выделение факторов]] | Этапы ФА. Выделение Факторов | seed | concept | [math, mephi, statistics, factor-analysis] |
@@ -473,16 +471,19 @@ total: 505
| Заметка | Aliases | Status | Type | Tags | | Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[Defense-in-depth]] | DiD, Глубокоэшелонированная защита | seed | concept | [networking, security, infosec, defense-in-depth] |
| [[Ethernet]] | Ethernet | stable | concept | [network, networking, ethernet] | | [[Ethernet]] | Ethernet | stable | concept | [network, networking, ethernet] |
| [[LAN vs WAN]] | LAN Vs WAN | stable | concept | [network, networking] | | [[LAN vs WAN]] | LAN Vs WAN | stable | concept | [network, networking] |
| [[MAC-адрес]] | MAC-адрес | stable | concept | [networking] | | [[MAC-адрес]] | MAC-адрес | stable | concept | [networking] |
| [[NAT]] | NAT | processing | concept | [network, networking, nat] | | [[NAT]] | NAT | processing | concept | [network, networking, nat] |
| [[Networking - MOC]] | Networking - MOC | stable | moc | [network, networking] | | [[Networking - MOC]] | Networking - MOC | stable | moc | [network, networking] |
| [[OAuth 2.0]] | OAuth 2, OAuth 2.0 | processing | concept | [web, security, oauth, authorization] | | [[OAuth 2.0]] | OAuth 2, OAuth 2.0 | processing | concept | [web, security, oauth, authorization] |
| [[SASE]] | Secure Access Service Edge | seed | concept | [networking, security, infosec, sase] |
| [[VLSM]] | VLSM | stable | concept | [network, networking] | | [[VLSM]] | VLSM | stable | concept | [network, networking] |
| [[VPN]] | VPN | stable | concept | [network, networking, vpn] | | [[VPN]] | VPN | stable | concept | [network, networking, vpn] |
| [[Zero Trust Network Access]] | ZTNA | seed | concept | [networking, security, infosec, ztna] |
| [[Базовая настройка коммутатора]] | Базовая Настройка Коммутатора | stable | concept | [network, networking] | | [[Базовая настройка коммутатора]] | Базовая Настройка Коммутатора | stable | concept | [network, networking] |
| [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | seed | concept | [networking, security, infosec] | | [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | processing | concept | [networking, security, infosec, vulnerability-scanning] |
| [[Веб-аутентификация и авторизация]] | Идентификация, аутентификация и авторизация, Веб-аутентификация | processing | concept | [web, security, authentication, authorization, oauth] | | [[Веб-аутентификация и авторизация]] | Идентификация, аутентификация и авторизация, Веб-аутентификация | processing | concept | [web, security, authentication, authorization, oauth] |
| [[Витая пара]] | Витая Пара | stable | concept | [network, networking] | | [[Витая пара]] | Витая Пара | stable | concept | [network, networking] |
| [[Иерархическая модель Cisco]] | Иерархическая Модель Cisco | stable | concept | [network, networking] | | [[Иерархическая модель Cisco]] | Иерархическая Модель Cisco | stable | concept | [network, networking] |
@@ -614,6 +615,9 @@ total: 505
|---------|---------|--------|------|------| |---------|---------|--------|------|------|
| [[31, 32 - Налоги]] | 31, 32 - Налоги | seed | knowledge | [study, taxes] | | [[31, 32 - Налоги]] | 31, 32 - Налоги | seed | knowledge | [study, taxes] |
| [[31, 32 - Налоги 1]] | 31, 32 - Налоги | seed | knowledge | [study, taxes] | | [[31, 32 - Налоги 1]] | 31, 32 - Налоги | seed | knowledge | [study, taxes] |
| [[CI-CD Конвейер для сборки и доставки продукта. Построение пайплайнов]] | — | seed | concept | [] |
| [[CIСD Конвейер для сборки и доставки продукта. Знакомство с GitlabCI и Jenkins]] | — | seed | concept | [] |
| [[gemini-code-1780921978295]] | — | — | — | [] |
| [[Git]] | Git | seed | concept | [] | | [[Git]] | Git | seed | concept | [] |
| [[Gitlab]] | Gitlab | seed | concept | [] | | [[Gitlab]] | Gitlab | seed | concept | [] |
| [[HW Eng17.03]] | HW Eng17.03 | seed | concept | [homework] | | [[HW Eng17.03]] | HW Eng17.03 | seed | concept | [homework] |
@@ -621,18 +625,22 @@ total: 505
| [[HW Eng24.03]] | — | seed | concept | [] | | [[HW Eng24.03]] | — | seed | concept | [] |
| [[Infrastructure]] | Infrastructure | seed | concept | [] | | [[Infrastructure]] | Infrastructure | seed | concept | [] |
| [[OSINT - Зачет]] | OSINT - Зачет | processing | knowledge | [mephi, study, osi] | | [[OSINT - Зачет]] | OSINT - Зачет | processing | knowledge | [mephi, study, osi] |
| [[Proxmox VE — local, local-lvm, LVM — source]] | — | seed | concept | [] |
| [[Rails 8 Migration Guide (finreport-analyzer)]] | — | seed | concept | [] | | [[Rails 8 Migration Guide (finreport-analyzer)]] | — | seed | concept | [] |
| [[Rails credentials edit не открывает редактор]] | Rails credentials edit не открывает редактор | seed | concept | [] | | [[Rails credentials edit не открывает редактор]] | Rails credentials edit не открывает редактор | seed | concept | [] |
| [[reader]] | — | seed | concept | [] | | [[reader]] | — | seed | concept | [] |
| [[Ruby - массивы]] | Ruby - массивы | seed | concept | [] | | [[Ruby - массивы]] | Ruby - массивы | seed | concept | [] |
| [[Ruby - основы языка]] | Ruby - основы языка | seed | concept | [] | | [[Ruby - основы языка]] | Ruby - основы языка | seed | concept | [] |
| [[Sudoers — source]] | sudoers, Настройка sudo | seed | guide | [linux, sudo, security] |
| [[Upgrade Homelab]] | Upgrade Homelab | processing | project | [homelab, hardware] | | [[Upgrade Homelab]] | Upgrade Homelab | processing | project | [homelab, hardware] |
| [[Алгоритм обратного распространения ошибки для произвольного числа слоев НС]] | Алгоритм обратного распространения ошибки для произвольного числа слоев НС | seed | concept | [] | | [[Алгоритм обратного распространения ошибки для произвольного числа слоев НС]] | Алгоритм обратного распространения ошибки для произвольного числа слоев НС | seed | concept | [] |
| [[Алгоритм обучения сети Кохонена]] | Алгоритм обучения сети Кохонена | seed | concept | [] | | [[Алгоритм обучения сети Кохонена]] | Алгоритм обучения сети Кохонена | seed | concept | [] |
| [[Анализ типа фин. устойчивости]] | Анализ типа фин. устойчивости | seed | concept | [] | | [[Анализ типа фин. устойчивости]] | Анализ типа фин. устойчивости | seed | concept | [] |
| [[Безопасность инфраструктуры. ZTNA, SASE, DiD]] | — | seed | concept | [] |
| [[В случае ошибок на принтере Kyocera FS-1020MFP]] | В случае ошибок на принтере Kyocera FS-1020MFP | seed | concept | [] | | [[В случае ошибок на принтере Kyocera FS-1020MFP]] | В случае ошибок на принтере Kyocera FS-1020MFP | seed | concept | [] |
| [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | processing | concept | [] | | [[Введение в сетевую безопасность]] | Введение в сетевую безопасность | processing | concept | [] |
| [[ВДО no кредитам]] | — | seed | concept | [] | | [[ВДО no кредитам]] | — | seed | concept | [] |
| [[Вопросы по Вебу]] | — | processing | concept | [] |
| [[Знания Ruby]] | Знания Ruby | seed | concept | [] | | [[Знания Ruby]] | Знания Ruby | seed | concept | [] |
| [[Идеи для статьи]] | Идеи Для Статьи | — | — | [] | | [[Идеи для статьи]] | Идеи Для Статьи | — | — | [] |
| [[Идея]] | — | seed | concept | [] | | [[Идея]] | — | seed | concept | [] |
@@ -651,6 +659,7 @@ total: 505
| [[Налоги - СР2]] | Налоги - СР2 | seed | knowledge | [study, taxes] | | [[Налоги - СР2]] | Налоги - СР2 | seed | knowledge | [study, taxes] |
| [[Нейронная сеть Кохонена]] | Нейронная сеть Кохонена | seed | concept | [] | | [[Нейронная сеть Кохонена]] | Нейронная сеть Кохонена | seed | concept | [] |
| [[Нейронная сеть хемминга]] | Нейронная сеть хемминга | seed | concept | [] | | [[Нейронная сеть хемминга]] | Нейронная сеть хемминга | seed | concept | [] |
| [[Обзор аналогов и обоснование инструментария системы учёта посещаемости студентов НИЯУ МИФИ]] | — | — | — | [] |
| [[Обратное распространение ошибки - вывод]] | Обратное распространение ошибки - вывод | seed | concept | [] | | [[Обратное распространение ошибки - вывод]] | Обратное распространение ошибки - вывод | seed | concept | [] |
| [[Основные команды git]] | Основные команды git | processing | concept | [] | | [[Основные команды git]] | Основные команды git | processing | concept | [] |
| [[Подготовка Speaking]] | Подготовка Speaking | seed | concept | [] | | [[Подготовка Speaking]] | Подготовка Speaking | seed | concept | [] |
@@ -825,3 +834,9 @@ total: 505
| [[Daily Note]] | — | — | daily | [daily] | | [[Daily Note]] | — | — | daily | [daily] |
| [[Study Item]] | — | seed | knowledge | [] | | [[Study Item]] | — | seed | knowledge | [] |
| [[Travel Item]] | — | seed | knowledge | [travel] | | [[Travel Item]] | — | seed | knowledge | [travel] |
## 99 System/Tools/agent-tools
| Заметка | Aliases | Status | Type | Tags |
|---------|---------|--------|------|------|
| [[AGENT_TOOLS]] | — | stable | guide | [agent, tooling, vault] |
View File
@@ -0,0 +1,57 @@
---
title: Agent tools для vault
status: stable
type: guide
tags: [agent, tooling, vault]
created: 2026-06-16
updated: 2026-06-16
aliases: []
---
# Agent tools для vault
Набор утилит для ИИ-агентов. Вывод — JSON, чтобы агент мог читать результат без парсинга Markdown-таблиц.
Запускать из корня vault:
```bash
python3 "99 System/Tools/agent-tools/vault_index.py" dump
```
## Команды
### `vault_index.py`
- `dump` — вывести все заметки из vault как JSON.
- `query --term "CI/CD" --limit 10` — найти близкие заметки.
- `rebuild` — пересобрать `99 System/INDEX.md`. Это write-команда.
### `vault_ingest.py`
- `scan` — показать файлы из `00 Inbox/` и `01 Ideas/`.
- `classify PATH` — эвристически классифицировать источник.
- `plan PATH` — выдать JSON-план ingest: назначение, похожие заметки, возможные wikilinks.
Команды read-only. Они не заменяют обязательный план и подтверждение перед workflow.
### `vault_audit.py`
- `broken-links` — найти wikilinks на несуществующие заметки.
- `orphans` — найти заметки без входящих ссылок.
- `source-links` — проверить wikilinks в `source`.
- `components` — найти компоненты графа ссылок.
- `all` — выполнить все проверки.
Команды read-only.
### `vault_frontmatter.py`
- `check [PATH ...]` — проверить обязательные поля и допустимые `status/type`.
- `normalize PATH ...` — нормализовать frontmatter выбранных файлов. Это write-команда.
## Правила для агентов
- Перед `ingest`, `wiki`, `audit`, `remind`, `query` сначала читать `99 System/INDEX.md`; скрипты помогают, но не отменяют правила `AGENTS.md`.
- Write-команды запускать только когда workflow уже подтверждён пользователем или правило явно требует обновить индекс.
- `vault_ingest.py plan` — вспомогательный план, не окончательное решение. Агент обязан проверить содержимое источника сам.
- После создания, удаления, перемещения заметки или изменения frontmatter запускать `vault_index.py rebuild`.
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
from collections import Counter, defaultdict
from vault_common import all_records, json_print, known_note_names, source_values, wikilinks
def broken_links(records: list[dict[str, object]]) -> list[dict[str, object]]:
known = known_note_names(records)
result = []
for record in records:
links = wikilinks(record["body"])
missing = sorted({link for link in links if link not in known})
if missing:
result.append({"path": record["path"], "missing": missing})
return result
def orphans(records: list[dict[str, object]]) -> list[dict[str, object]]:
incoming: Counter[str] = Counter()
aliases: dict[str, str] = {}
path_by_name: dict[str, str] = {}
for record in records:
path_by_name[record["name"]] = record["path"]
aliases[record["name"]] = record["name"]
aliases[record["path"]] = record["name"]
for alias in record["aliases"]:
aliases[alias] = record["name"]
for record in records:
for link in wikilinks(record["body"]):
target = aliases.get(link)
if target and target != record["name"]:
incoming[target] += 1
result = []
for name, path in sorted(path_by_name.items(), key=lambda item: item[1]):
if incoming[name] == 0 and not path.startswith("99 System/Archive/"):
result.append({"path": path, "name": name, "incoming": 0})
return result
def source_links(records: list[dict[str, object]]) -> dict[str, object]:
known = known_note_names(records)
linked = []
missing = []
plain = []
for record in records:
for source in source_values(record["frontmatter"].get("source")):
links = wikilinks(source)
if links:
for link in links:
item = {"path": record["path"], "source": source, "target": link}
if link in known:
linked.append(item)
else:
missing.append(item)
else:
plain.append({"path": record["path"], "source": source})
return {"linked": linked, "missing": missing, "plain": plain}
def graph_components(records: list[dict[str, object]]) -> list[dict[str, object]]:
known = known_note_names(records)
name_by_link = {}
for record in records:
name_by_link[record["name"]] = record["name"]
name_by_link[record["path"]] = record["name"]
for alias in record["aliases"]:
name_by_link[alias] = record["name"]
graph: dict[str, set[str]] = defaultdict(set)
path_by_name = {record["name"]: record["path"] for record in records}
for record in records:
name = record["name"]
graph.setdefault(name, set())
for link in wikilinks(record["body"]):
if link not in known:
continue
target = name_by_link.get(link)
if target and target != name:
graph[name].add(target)
graph[target].add(name)
seen = set()
components = []
for name in sorted(graph):
if name in seen:
continue
stack = [name]
component = []
seen.add(name)
while stack:
current = stack.pop()
component.append(current)
for next_name in graph[current]:
if next_name not in seen:
seen.add(next_name)
stack.append(next_name)
components.append(
{
"size": len(component),
"notes": [path_by_name[item] for item in sorted(component)[:20]],
"truncated": len(component) > 20,
}
)
return sorted(components, key=lambda item: item["size"])
def main() -> int:
parser = argparse.ArgumentParser(description="Agent-facing vault audit helper")
sub = parser.add_subparsers(dest="command", required=True)
sub.add_parser("broken-links")
sub.add_parser("orphans")
sub.add_parser("source-links")
sub.add_parser("components")
sub.add_parser("all")
args = parser.parse_args()
records = all_records()
if args.command == "broken-links":
json_print({"ok": True, "broken_links": broken_links(records)})
elif args.command == "orphans":
json_print({"ok": True, "orphans": orphans(records)})
elif args.command == "source-links":
json_print({"ok": True, "source_links": source_links(records)})
elif args.command == "components":
json_print({"ok": True, "components": graph_components(records)})
elif args.command == "all":
json_print(
{
"ok": True,
"broken_links": broken_links(records),
"orphans": orphans(records),
"source_links": source_links(records),
"components": graph_components(records),
}
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+279
View File
@@ -0,0 +1,279 @@
#!/usr/bin/env python3
from __future__ import annotations
import json
import re
from datetime import date, datetime
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[3]
INDEX_PATH = ROOT / "99 System" / "INDEX.md"
EXCLUDED_DIRS = {".git", ".trash", ".obsidian", ".smart-env"}
REQUIRED_FRONTMATTER = {"title", "status", "type", "tags", "created", "updated"}
VALID_STATUS = {"seed", "processing", "stable"}
VALID_TYPE = {"concept", "moc", "lab", "knowledge", "guide"}
try:
import yaml
except Exception: # pragma: no cover - depends on agent environment
yaml = None
def rel(path: Path) -> str:
return path.relative_to(ROOT).as_posix()
def json_print(data: Any) -> None:
print(json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True))
def note_files(include_index: bool = False) -> list[Path]:
files: list[Path] = []
for path in ROOT.rglob("*.md"):
parts = set(path.relative_to(ROOT).parts)
if parts & EXCLUDED_DIRS:
continue
if not include_index and path == INDEX_PATH:
continue
files.append(path)
return sorted(files, key=lambda p: rel(p).lower())
def split_frontmatter(text: str) -> tuple[dict[str, Any], str, str | None]:
lines = text.splitlines()
if not lines or lines[0].strip() != "---":
return {}, text, None
end = None
for index, line in enumerate(lines[1:], start=1):
if line.strip() == "---":
end = index
break
if end is None:
return {}, text, None
raw = "\n".join(lines[1:end])
body = "\n".join(lines[end + 1 :])
return parse_frontmatter(raw), body, raw
def parse_frontmatter(raw: str) -> dict[str, Any]:
if yaml is not None:
try:
data = yaml.safe_load(raw) or {}
return data if isinstance(data, dict) else {}
except Exception:
return {}
data: dict[str, Any] = {}
current_key: str | None = None
for line in raw.splitlines():
if not line.strip():
continue
list_item = re.match(r"^\s*-\s+(.*)$", line)
if list_item and current_key:
data.setdefault(current_key, [])
if isinstance(data[current_key], list):
data[current_key].append(clean_scalar(list_item.group(1)))
continue
pair = re.match(r"^([A-Za-zА-Яа-я0-9_-]+):\s*(.*)$", line)
if not pair:
continue
key, value = pair.group(1), pair.group(2).strip()
current_key = key
if value == "":
data[key] = [] if key in {"aliases", "tags"} else ""
elif value.startswith("[") and value.endswith("]"):
data[key] = parse_inline_list(value)
else:
data[key] = clean_scalar(value)
return data
def parse_inline_list(value: str) -> list[str]:
inner = value.strip()[1:-1].strip()
if not inner:
return []
return [clean_scalar(item.strip()) for item in inner.split(",") if item.strip()]
def clean_scalar(value: Any) -> str:
text = str(value).strip()
if len(text) >= 2 and text[0] == text[-1] and text[0] in {"'", '"'}:
return text[1:-1]
return text
def as_list(value: Any) -> list[str]:
if value is None or value == "":
return []
if isinstance(value, list):
return [clean_scalar(item) for item in value if clean_scalar(item)]
return [clean_scalar(value)]
def as_cell(value: Any, default: str = "") -> str:
if value is None or value == "":
return default
if isinstance(value, (date, datetime)):
return value.isoformat()
return str(value)
def tags_cell(value: Any) -> str:
tags = as_list(value)
return "[" + ", ".join(tags) + "]"
def note_record(path: Path) -> dict[str, Any]:
text = path.read_text(encoding="utf-8")
fm, body, raw_fm = split_frontmatter(text)
return {
"path": rel(path),
"name": path.stem,
"title": as_cell(fm.get("title"), ""),
"aliases": as_list(fm.get("aliases")),
"status": as_cell(fm.get("status")),
"type": as_cell(fm.get("type")),
"tags": as_list(fm.get("tags")),
"created": as_cell(fm.get("created"), ""),
"updated": as_cell(fm.get("updated"), ""),
"frontmatter": fm,
"has_frontmatter": raw_fm is not None,
"body": body,
}
def all_records() -> list[dict[str, Any]]:
return [note_record(path) for path in note_files()]
def wikilinks(text: str) -> list[str]:
text = strip_markdown_code(text)
links = []
for match in re.finditer(r"\[\[([^\]]+)\]\]", text):
target = match.group(1).split("|", 1)[0].split("#", 1)[0].strip()
if target:
links.append(target)
return links
def strip_markdown_code(text: str) -> str:
text = re.sub(r"```.*?```", "", text, flags=re.DOTALL)
text = re.sub(r"`[^`\n]*`", "", text)
return text
def known_note_names(records: list[dict[str, Any]]) -> set[str]:
names: set[str] = set()
for record in records:
names.add(record["name"])
names.add(record["path"])
names.update(record["aliases"])
return names
def tokenize(value: str) -> set[str]:
return {token.lower() for token in re.findall(r"[A-Za-zА-Яа-я0-9_+-]+", value)}
def search_records(records: list[dict[str, Any]], term: str, limit: int = 10) -> list[dict[str, Any]]:
query = tokenize(term)
if not query:
return []
scored = []
for record in records:
haystack = " ".join(
[
record["name"],
record["title"],
" ".join(record["aliases"]),
" ".join(record["tags"]),
record["path"],
]
)
tokens = tokenize(haystack)
score = len(query & tokens)
if term.lower() in haystack.lower():
score += 3
if score:
scored.append((score, record))
scored.sort(key=lambda item: (-item[0], item[1]["path"]))
return [compact_record(record) | {"score": score} for score, record in scored[:limit]]
def compact_record(record: dict[str, Any]) -> dict[str, Any]:
return {
"path": record["path"],
"name": record["name"],
"title": record["title"],
"aliases": record["aliases"],
"status": record["status"],
"type": record["type"],
"tags": record["tags"],
"created": record["created"],
"updated": record["updated"],
}
def write_index(records: list[dict[str, Any]]) -> None:
by_dir: dict[str, list[dict[str, Any]]] = {}
for record in records:
directory = str(Path(record["path"]).parent)
if directory == ".":
directory = "."
by_dir.setdefault(directory, []).append(record)
total = len(records)
lines = [
"---",
f"generated: {date.today().isoformat()}",
f"total: {total}",
"---",
"",
"# Vault Index",
"",
"## Статистика",
"",
"| Параметр | Значение |",
"|----------|----------|",
f"| Всего заметок | {total} |",
"",
]
for directory in sorted(by_dir, key=str.lower):
lines.extend(
[
f"## {directory}",
"",
"| Заметка | Aliases | Status | Type | Tags |",
"|---------|---------|--------|------|------|",
]
)
for record in sorted(by_dir[directory], key=lambda item: item["name"].lower()):
aliases = ", ".join(record["aliases"]) if record["aliases"] else ""
status = record["status"] or ""
note_type = record["type"] or ""
tags = "[" + ", ".join(record["tags"]) + "]"
lines.append(f"| [[{record['name']}]] | {aliases} | {status} | {note_type} | {tags} |")
lines.append("")
INDEX_PATH.write_text("\n".join(lines), encoding="utf-8")
def source_values(value: Any) -> list[str]:
if isinstance(value, list):
return [clean_scalar(item) for item in value if clean_scalar(item)]
if value:
return [clean_scalar(value)]
return []
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
from datetime import date
from pathlib import Path
from vault_common import (
REQUIRED_FRONTMATTER,
ROOT,
VALID_STATUS,
VALID_TYPE,
all_records,
as_list,
json_print,
note_record,
note_files,
rel,
split_frontmatter,
)
def resolve_paths(paths: list[str]) -> list[Path]:
if not paths:
return note_files()
result = []
for item in paths:
path = Path(item)
if not path.is_absolute():
path = ROOT / path
result.append(path)
return result
def validate_record(record: dict[str, object]) -> list[str]:
fm = record["frontmatter"]
issues = []
if not record["has_frontmatter"]:
issues.append("missing_frontmatter")
for field in sorted(REQUIRED_FRONTMATTER):
if field not in fm or fm[field] in (None, ""):
issues.append(f"missing_{field}")
if fm.get("status") and str(fm["status"]) not in VALID_STATUS:
issues.append("invalid_status")
if fm.get("type") and str(fm["type"]) not in VALID_TYPE:
issues.append("invalid_type")
if "tags" in fm and not isinstance(fm["tags"], list):
issues.append("tags_not_list")
if "aliases" in fm and fm["aliases"] not in (None, "") and not isinstance(fm["aliases"], list):
issues.append("aliases_not_list")
return issues
def check(paths: list[Path]) -> list[dict[str, object]]:
results = []
for path in paths:
record = note_record(path)
issues = validate_record(record)
if issues:
results.append({"path": rel(path), "issues": issues})
return results
def yaml_list(values: list[str]) -> list[str]:
if not values:
return ["[]"]
return [""] + [f" - {value}" for value in values]
def normalize(path: Path) -> dict[str, object]:
text = path.read_text(encoding="utf-8")
fm, body, _ = split_frontmatter(text)
title = fm.get("title") or path.stem
status = fm.get("status") if fm.get("status") in VALID_STATUS else "seed"
note_type = fm.get("type") if fm.get("type") in VALID_TYPE else "concept"
tags = as_list(fm.get("tags"))
aliases = as_list(fm.get("aliases"))
created = fm.get("created") or date.today().isoformat()
updated = date.today().isoformat()
lines = [
"---",
f"title: {title}",
f"status: {status}",
f"type: {note_type}",
"tags:" + ("\n".join(yaml_list(tags)) if tags else " []"),
f"created: {created}",
f"updated: {updated}",
"aliases:" + ("\n".join(yaml_list(aliases)) if aliases else " []"),
]
for key, value in fm.items():
if key in {"title", "status", "type", "tags", "created", "updated", "aliases"}:
continue
if isinstance(value, list):
lines.append(f"{key}:")
lines.extend(f" - {item}" for item in value)
elif value in (None, ""):
lines.append(f"{key}:")
else:
lines.append(f"{key}: {value}")
lines.extend(["---", "", body.lstrip("\n")])
path.write_text("\n".join(lines), encoding="utf-8")
return {"path": rel(path), "written": True}
def main() -> int:
parser = argparse.ArgumentParser(description="Agent-facing frontmatter helper")
sub = parser.add_subparsers(dest="command", required=True)
check_cmd = sub.add_parser("check")
check_cmd.add_argument("paths", nargs="*")
normalize_cmd = sub.add_parser("normalize")
normalize_cmd.add_argument("paths", nargs="+")
args = parser.parse_args()
if args.command == "check":
issues = check(resolve_paths(args.paths))
json_print({"ok": True, "issues": issues, "total_with_issues": len(issues)})
elif args.command == "normalize":
written = [normalize(path) for path in resolve_paths(args.paths)]
json_print({"ok": True, "written": written})
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
from vault_common import all_records, compact_record, json_print, search_records, write_index
def main() -> int:
parser = argparse.ArgumentParser(description="Agent-facing vault index helper")
sub = parser.add_subparsers(dest="command", required=True)
query = sub.add_parser("query", help="Search notes by term")
query.add_argument("--term", required=True)
query.add_argument("--limit", type=int, default=10)
sub.add_parser("dump", help="Dump note records as JSON")
sub.add_parser("rebuild", help="Rebuild 99 System/INDEX.md")
args = parser.parse_args()
records = all_records()
if args.command == "query":
json_print({"ok": True, "results": search_records(records, args.term, args.limit)})
elif args.command == "dump":
json_print({"ok": True, "total": len(records), "notes": [compact_record(r) for r in records]})
elif args.command == "rebuild":
write_index(records)
json_print({"ok": True, "written": "99 System/INDEX.md", "total": len(records)})
return 0
if __name__ == "__main__":
raise SystemExit(main())
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
from pathlib import Path
from vault_common import ROOT, all_records, json_print, rel, search_records, split_frontmatter, tokenize
INBOX_DIRS = [ROOT / "00 Inbox", ROOT / "01 Ideas"]
LIBRARY_HINTS = {
"ansible": "90 Library/DevOps/Ansible",
"ci": "90 Library/DevOps",
"cd": "90 Library/DevOps",
"cicd": "90 Library/DevOps",
"docker": "90 Library/Containers/Docker",
"git": "90 Library/DevOps",
"gitlab": "90 Library/DevOps",
"jenkins": "90 Library/DevOps",
"kubernetes": "90 Library/Containers/Kubernetes",
"linux": "90 Library/Linux",
"nginx": "90 Library/HomeLab/Proxy",
"rails": "90 Library/Programming/Ruby On Rails",
"ruby": "90 Library/Programming/Ruby",
"terraform": "90 Library/DevOps",
}
def read_path(path_arg: str) -> Path:
path = Path(path_arg)
if not path.is_absolute():
path = ROOT / path
return path
def scan() -> list[dict[str, object]]:
items = []
for directory in INBOX_DIRS:
if not directory.exists():
continue
for path in sorted(directory.iterdir(), key=lambda item: item.name.lower()):
if path.name == ".gitkeep" or not path.is_file():
continue
text = path.read_text(encoding="utf-8", errors="replace") if path.suffix == ".md" else ""
fm, body, _ = split_frontmatter(text)
items.append(
{
"path": rel(path),
"folder": rel(directory),
"suffix": path.suffix,
"title": fm.get("title") or path.stem,
"tags": fm.get("tags") or [],
"bytes": path.stat().st_size,
"classification": classify_text(path, body, fm),
}
)
return items
def classify_text(path: Path, body: str, frontmatter: dict[str, object]) -> dict[str, object]:
tags = frontmatter.get("tags") or []
if not isinstance(tags, list):
tags = [tags]
content = f"{path.stem}\n{' '.join(map(str, tags))}\n{body[:4000]}".lower()
tokens = tokenize(content)
idea_markers = {"идея", "idea", "business", "product", "стартап", "концепция", "задумка"}
if "01 Ideas" in rel(path) or tokens & idea_markers:
kind = "idea"
destination = "01 Ideas"
else:
kind = "library"
destination = "90 Library/Other"
for marker, folder in LIBRARY_HINTS.items():
if marker in content:
destination = folder
break
return {
"kind": kind,
"destination": destination,
"confidence": "heuristic",
}
def plan(path: Path) -> dict[str, object]:
text = path.read_text(encoding="utf-8", errors="replace")
fm, body, _ = split_frontmatter(text)
classification = classify_text(path, body, fm)
records = all_records()
terms = " ".join([path.stem, str(fm.get("title") or ""), " ".join(map(str, fm.get("tags") or []))])
similar = []
for item in search_records(records, terms, limit=30):
if item["path"] == rel(path):
continue
if item["path"].startswith(("03 Journal/", "99 System/Export/", "99 System/Cache/", "99 System/Template/")):
continue
similar.append(item)
if len(similar) == 8:
break
wikilinks = [item["name"] for item in similar[:5]]
return {
"path": rel(path),
"classification": classification,
"similar_notes": similar,
"suggested_wikilinks": wikilinks,
"write_policy": "read-only plan; agent must ask before workflow changes",
}
def main() -> int:
parser = argparse.ArgumentParser(description="Agent-facing ingest helper")
sub = parser.add_subparsers(dest="command", required=True)
sub.add_parser("scan", help="List files in 00 Inbox and 01 Ideas")
classify = sub.add_parser("classify", help="Classify one source file")
classify.add_argument("path")
plan_cmd = sub.add_parser("plan", help="Build JSON ingest plan for one source file")
plan_cmd.add_argument("path")
args = parser.parse_args()
if args.command == "scan":
json_print({"ok": True, "items": scan()})
elif args.command == "classify":
path = read_path(args.path)
text = path.read_text(encoding="utf-8", errors="replace")
fm, body, _ = split_frontmatter(text)
json_print({"ok": True, "path": rel(path), "classification": classify_text(path, body, fm)})
elif args.command == "plan":
json_print({"ok": True, "plan": plan(read_path(args.path))})
return 0
if __name__ == "__main__":
raise SystemExit(main())
+15 -1
View File
@@ -1,6 +1,10 @@
--- ---
created: 2026-04-20 created: 2026-04-20
updated: 2026-06-05 updated: 2026-06-16
title: Агент — контекст и инструкции для этого хранилища
status: stable
type: guide
tags: [agent, vault]
aliases: ["Агент — контекст и инструкции для этого хранилища"] aliases: ["Агент — контекст и инструкции для этого хранилища"]
--- ---
@@ -86,6 +90,16 @@ aliases: ["Агент — контекст и инструкции для это
- **Обновлять** после создания/удаления/перемещения заметки или изменения frontmatter. - **Обновлять** после создания/удаления/перемещения заметки или изменения frontmatter.
- **Как:** пройтись по всем `.md` (исключая `.trash`, `.obsidian`, `.git`, `.smart-env`, сам `99 System/INDEX.md`), извлечь frontmatter, перезаписать файл в том же формате (frontmatter с `generated`/`total`, таблицы по секциям). - **Как:** пройтись по всем `.md` (исключая `.trash`, `.obsidian`, `.git`, `.smart-env`, сам `99 System/INDEX.md`), извлечь frontmatter, перезаписать файл в том же формате (frontmatter с `generated`/`total`, таблицы по секциям).
## Agent tools
Утилиты для ИИ-агентов: `99 System/Tools/agent-tools/`.
- Документация: `99 System/Tools/agent-tools/AGENT_TOOLS.md`.
- Вывод скриптов — JSON, чтобы агент мог использовать результат без парсинга Markdown.
- Read-only команды можно использовать для ориентации: поиск по индексу, план ingest, audit, проверка frontmatter.
- Write-команды (`vault_index.py rebuild`, `vault_frontmatter.py normalize`) запускать только после подтверждённого workflow или когда правило явно требует обновить `INDEX.md`.
- Скрипты помогают, но не заменяют чтение исходников и правила этого файла.
## Workflow: Ingest ## Workflow: Ingest
Триггер: `ingest` или новые файлы в `00 Inbox/`. Триггер: `ingest` или новые файлы в `00 Inbox/`.