---
work_item: ORCH-103
stage: architecture
author_agent: architect
status: proposed
created_at: 2026-06-11
model_used: claude-opus-4-8
---

# adr-0038: Канон Bundled-тиража — `deploy/bundled/` + bootstrap + `BUNDLED_SETUP.md` (ORCH-103, 10b)

## Статус
Proposed

## Контекст

Эпик ORCH-10 (D5 «Масштаб»), тип **B — Bundled**: заказчик без собственной инфраструктуры
получает **весь стек одним комплектом** (орк + watchdog + Gitea + Plane CE ≈13–14 контейнеров) и
bootstrap, доводящий его до рабочего конвейера одним запуском. Фундамент готов: 10-common
(ORCH-101, adr-0036 — хост-параметризация/секреты/smoke) и Lite (ORCH-102, adr-0037 — док-канон
`docs/deployment/`). Корневой `docker-compose.yml` заморожен анти-дрейфом ORCH-102 (ровно 3
сервиса, запрет подстрок `plane`/`gitea`) → комплект обязан жить отдельным файлом.

Сквозной характер: вводится новый top-level каталог `deploy/` (дистрибутивы развёртывания),
новый канонический env-example и нормативы, обязательные для будущих задач эпика ORCH-10 и
любого агента, меняющего шаги тиража. Детальный пакет решений (D1…D11, исходы OQ-1…OQ-7 ТЗ) —
work-item ADR: `docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md`.

## Решение

1. **Новый top-level каталог `deploy/` — исполняемые дистрибутивы развёртывания** (дополняет
   `docs/deployment/` — инструкции). Bundled-комплект: **`deploy/bundled/docker-compose.yml`** —
   один самодостаточный compose всего стека с top-level `name: orchestrator-bundle` (project
   name = узнаваемый префикс томов/контейнеров; `container_name` не пиннится — нет коллизий с
   корневым compose на одном хосте). Staging-контур орка в bundle **отсутствует вовсе**; репо
   `orchestrator` в bundle-инсталляции не регистрируется → self-deploy-машинерия структурно спит
   (`SELF_HOSTING_REPO`-леафы не матчатся).
2. **Конфиг-слои:** `deploy/bundled/.env.example` — канон bundle-инфры (committed, плейсхолдеры;
   key-set-sync тест: каждая `${VAR}`-интерполяция bundle-compose имеет ключ в каноне) → live
   `deploy/bundled/.env` (авто-чтение compose из project dir — без `--env-file`-футгана; покрыт
   неякорным `.env` в `.gitignore`); runtime орка/watchdog — **корневые `.env`/`.env.watchdog`
   ровно по канону Lite** (REPLICATION §2 применим 1:1), в bundle-compose — `env_file:
   required: false` (первый `up` жив до сборки конфига). **Bootstrap — единственный писатель**
   всех трёх live-файлов (когерентность дублируемых ключей — механическая). Один факт = одно имя
   (ORCH-101 D1): существующие факты — существующие `ORCH_*`-имена; bundle-only — `BUNDLE_*`;
   внутренние креды Plane — upstream-имена.
3. **Состав/пиннинг:** Plane CE — зеркало официального selfhost-référence (upstream-имена
   сервисов/env); Gitea — `gitea/gitea` (не rootless). Пиннинг — **точный неподвижный тег
   литералом** (не `latest`, не интерполяция; digest не требуется); точные теги фиксирует
   developer по проверенному стенду; форму держит структурный тест.
4. **Сеть:** одна именованная bridge-сеть; машинный трафик — строго сервис-DNS
   (`http://orchestrator:8500/webhook/*`, `http://gitea:3000`, plane-proxy); `network_mode: host`
   в bundle не используется (ssh-деплой-пути неактивны: `ORCH_DEPLOY_SSH_HOST` пуст). Наружу —
   только человеческие порты (Plane proxy 8080 / Gitea 3000 / орк 8500; конфигурируемы);
   БД/брокер/minio не публикуются. Публичные URL — от `BUNDLE_PUBLIC_HOST` (split internal/public
   уже в конфиге орка). Мина Gitea закрывается явно: `GITEA__webhook__ALLOWED_HOST_LIST=orchestrator`.
5. **Bootstrap `scripts/bootstrap_bundle.py`:** python stdlib-only, без импортов из `src/**`;
   режимы `plan` (дефолт, ноль мутаций) / `apply` / `verify`; step-движок check→ensure
   (идемпотентность, resume = повторный запуск); exit `0/2/1`. Preflight fail-fast до мутаций
   (docker/порты/чистота томов по префиксу/RAM/диск; Claude CLI — warning). **Кирпичи не
   дублируются:** секреты — субпроцесс `gen_secrets.py`; статусы/лейблы/репо/вебхуки — строго
   `onboard_project.py apply`+`verify` (host-venv, канон ONBOARDING). Init Gitea — полностью
   автоматом (CLI в контейнере; branch protection НЕ настраивается — D10 ORCH-009/adr-0037 п.4);
   init Plane CE — честные **manual-step чекпоинты** (инструкция → подтверждение →
   API-верификация; прогрессивная автоматизация разрешена без смены контракта). Git-доступ
   агентов — HTTP token-remote (паттерн `_push_url`); ssh-контур не вводится. Секреты в
   логи не печатаются; delete-операций в скрипте нет вообще — teardown только документированной
   процедурой (`BUNDLED_SETUP` §13).
6. **Док-канон:** `docs/deployment/BUNDLED_SETUP.md` — 14 нормативных разделов по форме
   LITE_SETUP (fenced-команда + «Проверка:» PASS/FAIL, плейсхолдеры, общие шаги ссылками на
   LITE_SETUP/ONBOARDING/REPLICATION — канон не форкается), включая «Требования к хосту» с
   цифрами **по замеру** тестового развёртывания. REPLICATION §1: Type B → ✅ ORCH-103.
   **Норматив сопровождения:** изменил шаги Bundled-тиража → обнови BUNDLED_SETUP.md в том же PR.
7. **Анти-дрейф — постоянная CI-гарантия:** `tests/test_bundle_compose.py` /
   `test_bundled_setup_doc.py` / `test_bootstrap_script.py` (структурные, без docker/сети/LLM:
   состав сервисов, заморозка корневого compose, пины, key-set-sync, разделы дока, FORBIDDEN —
   импортом из `test_no_host_hardcodes.py`, секрет-эвристика, ссылки на кирпичи, отсутствие
   delete-операций, unit чистых функций preflight/плана, exit-контракт).

### Что НЕ меняется
`src/**`, корневой `docker-compose.yml`, `Dockerfile`, `.gitea/workflows/**`, `onboarding/**`,
промпты `.openclaw/agents/**`; `STAGE_TRANSITIONS`, состав `QG_CHECKS`, семантика `check_*`,
machine-verdict ключи, схема БД — байт-в-байт. Kill-switch не вводится (активация — только явный
запуск оператора на целевом хосте, паттерн ORCH-009). Прод-контейнер в рамках задачи не
рестартуется; наши данные/секреты не переносятся (stateless, решение Владельца 10.06).

## Альтернативы
- **Расширение корневого compose (профиль `bundled`)** — отвергнуто: заморожен анти-дрейфом
  ORCH-102/нормативом «compose не форкается»; смешение дистрибутива с боевым контуром.
- **Include-композиция / live-env через `--env-file`** — отвергнуто: лишние степени свободы
  запуска, молчаливые дефолты при забытом флаге.
- **Орк в bundle на host-network + `host-gateway`** — отвергнуто: хост-сеть нужна была
  ssh-деплой-контуру нашего хоста, который в bundle спит; bridge даёт чистые двунаправленные
  сервис-DNS-URL.
- **Digest-пиннинг / rootless-Gitea / ssh-доступ агентов / bash-bootstrap / reset-режим
  скрипта** — отвергнуты (см. work-item ADR-001, «Альтернативы»).

## Последствия
- Эпик ORCH-10 закрыт по обоим типам: A (Lite, инструкция) + B (Bundled, комплект); заказчик
  без инфраструктуры разворачивает конвейер «под ключ».
- Цена: пиннованные версии Plane/Gitea стареют (апгрейд — отдельные задачи); manual-step Plane CE
  размывают «одну команду» — неустранимо честно (нет API), митигировано контрактом чекпоинта;
  двойной `.env`-слой — под единственным писателем-bootstrap и key-sync тестом.
- Откат: удалить `deploy/`, bootstrap, BUNDLED_SETUP.md, три тест-модуля, строку REPLICATION §1 —
  состояние 1:1 (docs+scripts+tests, без миграций).

## Связи
adr-0036 (ORCH-101 — фундамент 10-common: параметризация, gen_secrets, REPLICATION/smoke),
adr-0037 (ORCH-102 — док-канон `docs/deployment/`, compose-подмножество, запрет branch
protection), adr-0035 (ORCH-009 — onboarding-CLI: 22 статуса, manual-step паттерн, `_push_url`,
D10), adr-0027/INV-4 (merge-актор — основание норматива Gitea), adr-0001
(`SELF_HOSTING_REPO`-конвенция — почему self-гейты в bundle спят). Детально —
`docs/work-items/ORCH-103/06-adr/ADR-001-bundled-stack-compose-and-bootstrap.md`,
`07-infra-requirements.md`, `10-tech-risks.md`.
