--- title: Архитектура фреймворка description: Четыре опоры репозиториев узла, субмодуль commons и поток диспетчеризации. weight: 11 --- Фреймворк apex делит автоматизацию флота между одним общим репозиторием ([«commons»](/apex/glossary#commons)) и отдельными репозиториями для каждого [узла](/apex/glossary#node). В commons сосредоточено всё общее для флота: [движок](/apex/glossary#engine), стандартные [действия](/apex/glossary#action) и [ядровая композиция](/apex/glossary#core-composition). Репозиторий узла содержит специфичные для хоста данные: его идентичность, хостовые [конфигурации](/apex/glossary#configs), сервисные [композиции](/apex/glossary#compositions) и локальные [проприетарии](/apex/glossary#proprietaries). ## Четыре опоры {#pillars} Каждый репозиторий узла имеет одинаковую структуру: {{< filetree/container >}} {{< filetree/folder name="." state="open" >}} {{< filetree/folder name="commons" state="closed" >}}{{< /filetree/folder >}} {{< filetree/folder name="proprietaries" state="closed" >}}{{< /filetree/folder >}} {{< filetree/folder name="configs" state="closed" >}}{{< /filetree/folder >}} {{< filetree/folder name="compositions" state="closed" >}}{{< /filetree/folder >}} {{< filetree/file name="node.env" >}} {{< filetree/file name="init.sh" >}} {{< /filetree/folder >}} {{< /filetree/container >}} | Опора | Содержимое | Владение | |---|---|---| | `commons/` | Git-субмодуль → `apex.ermnvldmr.com`, закреплённый на релизном теге | Общее, версионируемое | | `proprietaries/` | Узло-локальные действия, библиотеки и исходники инструментов ([накладываются оверлеем](/apex/glossary#overlay) поверх commons) | Узел | | `configs/` | Исходники конфигурации хоста: `ufw/`, `cron/crontab`, `systemd/`, `crowdsec/` | Узел | | `compositions/` | По одному каталогу на проект, ядровая обёртка `apex/` и реестр адресации | Узел | Фиксация субмодуля — версионный контракт фреймворка: узел выполняет именно ту версию движка и ядровой композиции, которая зафиксирована в его gitlink. Обновление узла выполняется явно: достаточно переключиться на новый тег внутри `commons/` и закоммитить изменённый указатель. ## Поток диспетчеризации {#dispatch} Лаунчер `commons/apex` работает с локальным чекаутом; передавать узел в качестве аргумента не нужно нигде. ```mermaid flowchart TD A["apex <group>/<action> [args]"] --> B{Схема размещения} B -- "каталог commons/ с node.env рядом" --> C[Checkout узла:
оверлей = proprietaries/actions] B -- иначе --> D[Автономный commons:
без оверлея] C --> E["Разрешение оверлея
(сначала proprietaries)"] D --> E E --> F["Разрешение идентичности
(FQDN + node.env)"] F --> G["Построение ctx
(log, sys, tpl, host, ufw,
crowdsec, systemd, notify)"] G --> H["разбор аргументов (Meta)"] H --> I["run(ctx, args)"] ``` Движок определяет режим работы по своему расположению на диске. Если он запущен из каталога `commons/`, а в родительском каталоге присутствует `node.env`, то родительский каталог принимается за корень репозитория узла, а `proprietaries/actions/` становится деревом оверлея. При любом другом расположении commons считается автономным чекаутом и выступает собственным корнем. Команда перечисления (`apex --help`) никогда не импортирует модули действий: краткие описания и маркеры отключения извлекаются статически с помощью модуля `ast`. При диспетчеризации загружается ровно один модуль по вычисленному пути. Подробнее — в разделе [Концепция: Действия и оверлей](/apex/concepts/actions-and-overlay). ## Модель «capture-up» {#capture-up} Репозитории узлов служат реестрами состояния, а не просто источниками конфигурации: хост сам фиксирует то, что выполняет на практике. 1. Регулярная задача `sync/repository` на хосте коммитит рабочее дерево в ветку `sync/` (включая указатель субмодуля commons), делает rebase поверх `origin/main` и выполняет push с флагом `--force-with-lease`. 2. Запланированное CI-задание в репозитории узла вливает ветку `sync/` обратно в `main` от имени бота. Благодаря циклу [«capture-up»](/apex/glossary#capture-up) любой дрейф конфигурации на хосте превращается в коммит: изменение наглядно отображается в diff и легко откатывается, исключая незаметные расхождения. Если субмодуль commons имеет незакоммиченные изменения («грязный») или каталог `compositions/` отсутствует, синхронизация прерывается без коммита, поэтому повреждённое состояние чекаута никогда не попадёт в историю. > [!NOTE] > CI-задание слияния клонирует репозиторий *без* субмодулей: субмодуль commons подключён по SSH-URL, недоступному стандартному токену CI, а самому слиянию требуется лишь обновить указатель gitlink. ## Проектные ограничения {#constraints} - **Только стандартная библиотека (stdlib).** Движок работает на штатном Python 3 из Debian — без `pip` и сторонних зависимостей. Каталог `vendor/` зарезервирован для зафиксированного чистого Python-кода, но по умолчанию поставляется пустым. - **Ручные compose-файлы.** Никаких промежуточных манифестов или кодогенерации: файлы compose служат единственным источником истины. - **Автоматическая проверка вместо соглашений.** Всё, что можно проверить или применить автоматически, выполняется действием (проверка якорных IP, откат crontab, самовосстановление блоков-маркеров), а не остаётся на совести документации. --- **См. также:** - [Концепция: Действия и оверлей](/apex/concepts/actions-and-overlay) - [Концепция: Разрешение идентичности узла](/apex/concepts/node-identity) - [Руководство: Сборка репозитория узла](/apex/guides/node-repository)