Архитектура и схемы
Технический обзор: как связаны Keeper, агенты, PostgreSQL, Redis и Vault, и как прогон проходит путь от описания желаемого состояния до применения на хосте.
Раздел предполагает, что вы знакомы со словарём из Концепций (Keeper, Souls, Destiny, Soulprint, Essence, Coven, Incarnation).
Топология
Заголовок раздела «Топология»В центре — Keeper-кластер: один или несколько одинаковых stateless-инстансов поверх общих PostgreSQL (холодное состояние) и Redis (presence, координация). Любой инстанс обслуживает любой запрос. На управляемых хостах — агенты Soul, которые сами инициируют соединение к Keeper-у.
flowchart TB
Op["Оператор / CI<br/>OpenAPI · MCP · soulctl"] -->|HTTP / mTLS| KC
subgraph KC["Keeper-кластер — HA, stateless"]
direction TB
KK["keeper × N инстансов"]
R[("Redis<br/>presence · lease · pub/sub")]
PG[("PostgreSQL<br/>реестры · каталог · журналы")]
KK --- R
KK --- PG
end
KC -->|"pull: Soul инициирует стрим<br/>gRPC bidi-stream поверх mTLS"| SD["soul<br/>демон, агент"]
KC -->|"push: Keeper идёт сам<br/>по SSH, oneshot"| HO["host<br/>без агента"]
SL["soul-lint · CI / dev<br/>офлайн-валидация Destiny / сценариев / Essence — без Keeper и без сети"]
Внешняя инфраструктура (PostgreSQL, Redis, Vault) — это компоненты, которые вы поднимаете и эксплуатируете сами; Soul Stack их не поставляет в комплекте. Обязательный контур — PostgreSQL + Redis + Vault (все три; Keeper проверяет каждый на старте и не стартует без любого из них).
Транспорт: стрим инициирует Soul
Заголовок раздела «Транспорт: стрим инициирует Soul»Связь Keeper ↔ Soul — долгоживущий gRPC bidirectional stream поверх mTLS. Принципиальный момент: соединение инициирует агент, а не сервер.
- На управляемых хостах не нужно открывать входящие порты — агент сам дозванивается до Keeper-а и держит стрим.
- По этому же стриму идут в обе стороны: задания на применение (Keeper → Soul) и отчёты о прогоне, события задач, отчёт Soulprint (Soul → Keeper).
- Отдельного heartbeat-сообщения нет: gRPC keepalive и любое прикладное сообщение по стриму обновляют «время последней активности» агента.
Аутентификация на стриме — взаимная (mTLS): Keeper проверяет клиентский сертификат агента, агент — серверный сертификат Keeper-а. Оба сертификата выпускаются из одного PKI-корня.
HA и горизонтальное масштабирование
Заголовок раздела «HA и горизонтальное масштабирование»Keeper-инстансы stateless и взаимозаменяемы. Разделение хранилищ — ключ к масштабированию:
- PostgreSQL — холодное хранилище. Реестры агентов и операторов, каталог сервисов, runtime-инстансы (incarnation), журналы прогонов и аудит. Единственный источник истины состояния кластера.
- Redis — горячий слой. Presence (кто сейчас на связи), lease на идентификаторы агентов, heartbeat-кэш, pub/sub-координация между инстансами Keeper. Волатильные данные не пишутся синхронно в PostgreSQL — они живут в Redis.
Фоновую уборку ведёт Reaper — задача внутри Keeper-а: чистит просроченные онбординг-токены, зомби-записи и устаревший TLS-материал агентов. В кластере Reaper работает только на одном инстансе одновременно — лидер выбирается через Redis-lease.
Два режима применения: pull и push
Заголовок раздела «Два режима применения: pull и push»Один и тот же исполняемый файл soul и один и тот же набор модулей работают в двух режимах доставки:
| Режим | Кто инициирует | Как работает |
|---|---|---|
| pull | агент | soul запущен как демон, держит долгоживущий gRPC-стрим к Keeper-у, ждёт и применяет задания. Для постоянно управляемых хостов. |
| push | Keeper | Keeper по SSH доходит до хоста, на котором агент не установлен постоянно, выполняет применение разово (oneshot) и ничего постоянного не оставляет, кроме самих изменений. |
В обоих режимах модули применяются одинаково — режим определяет только способ доставки, не логику применения. В реестре push-хост и pull-хост — это записи одного типа, различающиеся полем транспорта; переключение между режимами — смена одного поля без потери истории.
Поток применения: от описания до отчёта
Заголовок раздела «Поток применения: от описания до отчёта»Принципиальная особенность Soul Stack: рендер выполняется на стороне Keeper-а, не на хосте. Агент не тянет шаблонизатор, не резолвит секреты и не имеет доступа к Vault — он получает уже готовые к применению задачи.
flowchart TB
Op["Оператор — создаёт / обновляет incarnation<br/>(через OpenAPI / soulctl)"] --> K1
K1["Keeper<br/>1. Резолв сервиса — git-источник + ref<br/>2. Резолв хостов — coven / where<br/>3. Рендер Destiny — vault-resolve → input-validation → CEL → text/template"]
K1 -->|"ApplyRequest — готовые задачи<br/>по gRPC-стриму поверх mTLS"| S1
S1["Soul<br/>4. Применяет модули по очереди, идемпотентно<br/>→ события задач и отчёт о прогоне"]
S1 -->|"отчёт о прогоне (RunResult)"| K2
K2["Keeper<br/>5. Все хосты успешно → state-commit в PostgreSQL<br/>Хоть один упал → incarnation: error_locked (состояние не коммитится)"]
Фазы рендера (на стороне Keeper-а)
Заголовок раздела «Фазы рендера (на стороне Keeper-а)»Все YAML-источники проходят одинаковый конвейер. Порядок фиксирован, фазы не перемешиваются:
- vault-resolve — ссылки на секреты в параметрах заменяются на значения; делается до вычисления выражений.
- input-validation — эффективный вход оператора сверяется с объявленным контрактом (типы, обязательность,
pattern/enum). - CEL-render — вычисляются выражения в YAML: условия таргетинга, интерполяция значений (маркер
${ … }), чтение фактов хоста. - text/template-render — рендер файлов из шаблонов (
.tmpl) с явно поднятыми значениями.
Почему рендер на сервере, а не на хосте:
- Безопасность. Секреты резолвятся централизованно; на хост уезжают только нужные значения, у агента нет Vault-токенов и доступа к секрет-хранилищу.
- Лёгкий агент. Агент не тянет движок выражений и шаблонов — он применяет готовые задачи.
- Единая точка истины. Контекст рендера (факты хостов, параметры, секреты) собирается там, где он целиком доступен.
Подробнее про модель выражений и границу движков — в разделе DSL → Шаблонизатор.
Модель применения и атомарность
Заголовок раздела «Модель применения и атомарность»Состояние incarnation в базе обновляется только после того, как все целевые хосты успешно отработали — это cross-host барьер:
- прогон дожидается завершения всех задач на всех хостах;
- только после барьера результат сценария коммитится в
incarnation.state(PostgreSQL); - если хоть одна задача хоть на одном хосте упала — состояние не коммитится, incarnation переходит в
error_locked, и следующая операция отклоняется до явного разрешения оператора.
Это сознательный fail-fast: частичные применения не «проскакивают» молча, и зафиксированное состояние всегда соответствует факту на хостах.
Идентичность агента
Заголовок раздела «Идентичность агента»Идентичность Soul строится на двух сущностях:
- SID — идентификатор агента, равный FQDN хоста. Это автоматически даёт дедупликацию при переустановке агента на тот же хост. Обратная сторона: переименование FQDN — это миграция (in-place rename не поддерживается).
- SoulSeed — mTLS-пара (сертификат + приватный ключ), которой агент аутентифицируется на стриме. Выпускается через CSR: приватный ключ генерируется на хосте и никогда его не покидает. В базе хранится только отпечаток сертификата — без PEM и без приватных ключей. SoulSeed регулярно ротируется по живому стриму.
Онбординг идёт в два хода: оператор регистрирует хост и получает одноразовый bootstrap-токен, затем агент на хосте обменивает токен на SoulSeed. Пошаговый онбординг — в Quick Start.
Где что хранится: git против базы
Заголовок раздела «Где что хранится: git против базы»| Артефакт | Где живёт | Почему |
|---|---|---|
| Service / Destiny / Module | git | Это код: ревью, версионирование через git-ref, история изменений. |
| Incarnation / Coven | PostgreSQL | Это runtime-состояние: полный operator-flow через OpenAPI/MCP, меняется в эксплуатации. |
| Provider / Profile | PostgreSQL | Реестр cloud-провайдеров и профилей: полный operator-flow через REST (/v1/providers, /v1/profiles) / MCP / UI под RBAC provider.* / profile.*. На roadmap — готовые soul-cloud-* драйверы (в базовую поставку пока не входят). См. core.cloud. |
Версия Service / Destiny / Module — это git-ref (тег или ветка), а не поле в манифесте: зависимости пинуются точным ref-ом, без semver-диапазонов.
Что дальше
Заголовок раздела «Что дальше»- Компоненты — детальный разбор Keeper, Soul, soulctl, soul-lint.
- DSL — как описывать Destiny, сценарии и Essence.
- Конфигурация — настройка Keeper-а и агента.
- Операции — день второй: обновления, мониторинг, восстановление.