Добавлены root-only bootstrap и read-only drift verifier, inventory и операционные документы.
Добавлены Caddy HTTPS proxy, приватные Compose-сети и Docker secret для review signing key.
Добавлен холодный backup с проверкой остановленных сервисов, SQLite integrity, SHA-256, Git revision, deployment hashes и image IDs.
Добавлено применение staged restore только на помеченном recovery-хосте; оно сверяет identity хоста, чистый Git revision, пустые пути/volumes, права секретов и оставляет сервисы остановленными.
Добавлены root-owned read-only verifier и guarded rollback helper с точными sudoers-командами; общего sudo и доступа к Docker codex не получает.
Gitea Actions проверяет Python suites, shell syntax, host guards, rollback helper, Compose/Caddyfile и собирает production images. Публикация и deploy выключены.
ACCESS и CURRENT_STATE отражают WSL2 и проверки VPS из текущего сеанса.
Проверки
Backend: 37 passed, 1 OpenSearch integration skipped без AKYLDASH_OPENSEARCH_URL.
Host identity guard, verifier argument guard, Bash/Python syntax, CI YAML и git diff checks прошли.
Локальный изолированный Compose smoke test: OpenSearch, backend, Caddy healthy; GET /review через Caddy вернул HTTP 200. Временные containers, networks и volumes удалены.
Production Caddyfile прошёл caddy validate.
Ограничения
Read-only SSH подтвердил, что на VPS у codex нет sudo, Docker и Git. Production bootstrap, установка helpers, backup/restore на реальных данных и публичный TLS не проверялись. Gitea CI для текущего head 9084fd4 всё ещё Waiting to run; runner availability API требует владельца репозитория (403). Требуется ручное ревью; PR не объединён.
## Что изменено
- Добавлены root-only bootstrap и read-only drift verifier, inventory и операционные документы.
- Добавлены Caddy HTTPS proxy, приватные Compose-сети и Docker secret для review signing key.
- Добавлен холодный backup с проверкой остановленных сервисов, SQLite integrity, SHA-256, Git revision, deployment hashes и image IDs.
- Добавлено применение staged restore только на помеченном recovery-хосте; оно сверяет identity хоста, чистый Git revision, пустые пути/volumes, права секретов и оставляет сервисы остановленными.
- Добавлены root-owned read-only verifier и guarded rollback helper с точными sudoers-командами; общего sudo и доступа к Docker codex не получает.
- Gitea Actions проверяет Python suites, shell syntax, host guards, rollback helper, Compose/Caddyfile и собирает production images. Публикация и deploy выключены.
- ACCESS и CURRENT_STATE отражают WSL2 и проверки VPS из текущего сеанса.
## Проверки
- Backend: 37 passed, 1 OpenSearch integration skipped без AKYLDASH_OPENSEARCH_URL.
- Telegram: 6 passed; backup/restore: 9 passed; rollback: 3 passed.
- Host identity guard, verifier argument guard, Bash/Python syntax, CI YAML и git diff checks прошли.
- Локальный изолированный Compose smoke test: OpenSearch, backend, Caddy healthy; GET /review через Caddy вернул HTTP 200. Временные containers, networks и volumes удалены.
- Production Caddyfile прошёл caddy validate.
## Ограничения
Read-only SSH подтвердил, что на VPS у codex нет sudo, Docker и Git. Production bootstrap, установка helpers, backup/restore на реальных данных и публичный TLS не проверялись. Gitea CI для текущего head 9084fd4 всё ещё Waiting to run; runner availability API требует владельца репозитория (403). Требуется ручное ревью; PR не объединён.
Проверил PR независимо. Нужны исправления в процедурах первого развёртывания: сейчас описанный порядок нельзя выполнить с добавленным архиватором, а README production противоречит новой схеме доступа и резервного копирования. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (9), rollback (3), оба shell guard теста. На production запуск и восстановление не проверялись.
Проверил PR независимо. Нужны исправления в процедурах первого развёртывания: сейчас описанный порядок нельзя выполнить с добавленным архиватором, а README production противоречит новой схеме доступа и резервного копирования. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (9), rollback (3), оба shell guard теста. На production запуск и восстановление не проверялись.
P2 — инструкция production устарела и противоречит этой же ветке. Здесь сказано, что проверенного backup-скрипта нет, хотя PR добавляет infra/backup/archive.py и процедуру в docs/operations/BACKUP.md. Шаги ниже запускают docker compose без sudo и используют .env в checkout, тогда как новая схема требует root-действий, не даёт codex доступ к Docker и хранит env в /etc/akyldash/production.env. Обновите README до одного выполнимого порядка для владельца VPS.
**P2 — инструкция production устарела и противоречит этой же ветке.** Здесь сказано, что проверенного backup-скрипта нет, хотя PR добавляет `infra/backup/archive.py` и процедуру в `docs/operations/BACKUP.md`. Шаги ниже запускают `docker compose` без `sudo` и используют `.env` в checkout, тогда как новая схема требует root-действий, не даёт `codex` доступ к Docker и хранит env в `/etc/akyldash/production.env`. Обновите README до одного выполнимого порядка для владельца VPS.
P1 — первый backup по этой инструкции невозможен.infra/backup/archive.py:138–163 требует, чтобы у всех сервисов уже существовали остановленные Compose-контейнеры. На чистом VPS до первого up/create их нет, поэтому create завершается ошибкой Compose has no containers. Кроме того, backup до загрузки индекса и создания admin не покрывает эти данные. Добавьте выполнимый порядок для первоначального создания контейнеров/томов и отдельный проверенный backup после инициализации данных.
**P1 — первый backup по этой инструкции невозможен.** `infra/backup/archive.py:138–163` требует, чтобы у всех сервисов уже существовали остановленные Compose-контейнеры. На чистом VPS до первого `up`/`create` их нет, поэтому `create` завершается ошибкой `Compose has no containers`. Кроме того, backup до загрузки индекса и создания admin не покрывает эти данные. Добавьте выполнимый порядок для первоначального создания контейнеров/томов и отдельный проверенный backup после инициализации данных.
Повторно проверил исправления на текущем head. Замечание про устаревший production README исправлено, новый архиватор корректно различает пустое хранилище и отсутствие контейнеров в покрытых тестами случаях. Первый запуск с предусмотренным инструкцией заранее загруженным корпусом всё ещё не имеет выполнимого безопасного порядка, поэтому оставляю Request changes. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (15), rollback (3), shell guard тесты и git diff --check.
Повторно проверил исправления на текущем head. Замечание про устаревший production README исправлено, новый архиватор корректно различает пустое хранилище и отсутствие контейнеров в покрытых тестами случаях. Первый запуск с предусмотренным инструкцией заранее загруженным корпусом всё ещё не имеет выполнимого безопасного порядка, поэтому оставляю Request changes. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (15), rollback (3), shell guard тесты и `git diff --check`.
P1 — первый запуск с заранее загруженным корпусом по этой процедуре блокируется. Строки 24–25 требуют положить minjust-normalized в DATA_ROOT до запуска. На новом VPS именованные тома Compose ещё не созданы; compose_sources() видит непустой bind mount и отсутствующие тома и выдаёт persistent Compose volumes are missing while data exists (infra/backup/archive.py:187–192). Это прямо зафиксировано новым тестом test_existing_data_with_missing_named_volume_is_rejected_as_incomplete. Предложенное здесь create and verify a backup выполнить невозможно, а пропускать backup при существующем корпусе инструкция запрещает. Нужен описанный и проверенный путь первоначального создания пустых томов и backup корпуса до up либо другой безопасный порядок.
**P1 — первый запуск с заранее загруженным корпусом по этой процедуре блокируется.** Строки 24–25 требуют положить `minjust-normalized` в `DATA_ROOT` до запуска. На новом VPS именованные тома Compose ещё не созданы; `compose_sources()` видит непустой bind mount и отсутствующие тома и выдаёт `persistent Compose volumes are missing while data exists` (`infra/backup/archive.py:187–192`). Это прямо зафиксировано новым тестом `test_existing_data_with_missing_named_volume_is_rejected_as_incomplete`. Предложенное здесь `create and verify a backup` выполнить невозможно, а пропускать backup при существующем корпусе инструкция запрещает. Нужен описанный и проверенный путь первоначального создания пустых томов и backup корпуса до `up` либо другой безопасный порядок.
P2 — после инициализации данных процедура оставляет первый релиз без восстановимой точки. Для действительно пустого хоста backup пропускается, затем создаются индекс и admin, но шаги не останавливают сервисы и не создают проверенный backup новых SQLite/OpenSearch данных перед завершением первого развёртывания. Добавьте обязательный холодный backup и проверку restore после этой инициализации, прежде чем считать production готовым.
**P2 — после инициализации данных процедура оставляет первый релиз без восстановимой точки.** Для действительно пустого хоста backup пропускается, затем создаются индекс и admin, но шаги не останавливают сервисы и не создают проверенный backup новых SQLite/OpenSearch данных перед завершением первого развёртывания. Добавьте обязательный холодный backup и проверку restore после этой инициализации, прежде чем считать production готовым.
Проверил коммит 752266d. Ранее отмеченный случай с заранее загруженным корпусом теперь обрабатывается и покрыт тестом. Однако допуск отсутствующих томов создаёт риск неполного backup уже работавшей установки, а обязательный backup после первой инициализации индекса и admin всё ещё не добавлен. Поэтому оставляю Request changes. Локальные suites прошли: backend 37 (1 skip), Telegram 6, backup/restore 15, rollback 3; shell guards и git diff --check прошли.
Проверил коммит 752266d. Ранее отмеченный случай с заранее загруженным корпусом теперь обрабатывается и покрыт тестом. Однако допуск отсутствующих томов создаёт риск неполного backup уже работавшей установки, а обязательный backup после первой инициализации индекса и admin всё ещё не добавлен. Поэтому оставляю Request changes. Локальные suites прошли: backend 37 (1 skip), Telegram 6, backup/restore 15, rollback 3; shell guards и `git diff --check` прошли.
P2 — нет обязательного backup после первой инициализации. Порядок для полностью пустого хоста пропускает backup, затем создаёт индекс и admin, но не требует остановить сервисы, сделать и проверить архив получившихся SQLite/OpenSearch данных и пробное staged restore. В docs/operations/DEPLOYMENT.md последовательность заканчивается проверкой /review и записью image ID. Добавьте эту процедуру до признания первого развёртывания завершённым.
**P2 — нет обязательного backup после первой инициализации.** Порядок для полностью пустого хоста пропускает backup, затем создаёт индекс и admin, но не требует остановить сервисы, сделать и проверить архив получившихся SQLite/OpenSearch данных и пробное staged restore. В `docs/operations/DEPLOYMENT.md` последовательность заканчивается проверкой `/review` и записью image ID. Добавьте эту процедуру до признания первого развёртывания завершённым.
P1 — отсутствующий том после прежнего запуска теперь молча попадает в «проверенный» backup как пустой. Условие допускает None для любого named volume всякий раз, когда контейнеров нет, без признака именно первого развёртывания. Например, после docker compose down и потери akyldash_opensearch-data оставшиеся SQLite/корпус в DATA_ROOT делают persistent_data=True; код создаст архив с пустым opensearch/ и успешной SHA-проверкой вместо отказа из-за утраченного индекса. Новый тест подтверждает такое поведение для существующих данных и отсутствующего тома. Разрешайте пустые компоненты только при явно проверенном первом запуске; в остальных случаях прекращайте backup как неполный.
**P1 — отсутствующий том после прежнего запуска теперь молча попадает в «проверенный» backup как пустой.** Условие допускает `None` для любого named volume всякий раз, когда контейнеров нет, без признака именно первого развёртывания. Например, после `docker compose down` и потери `akyldash_opensearch-data` оставшиеся SQLite/корпус в `DATA_ROOT` делают `persistent_data=True`; код создаст архив с пустым `opensearch/` и успешной SHA-проверкой вместо отказа из-за утраченного индекса. Новый тест подтверждает такое поведение для существующих данных и отсутствующего тома. Разрешайте пустые компоненты только при явно проверенном первом запуске; в остальных случаях прекращайте backup как неполный.
Повторно проверил исправления на head 3b41d1f. Оба прежних замечания устранены: отсутствие именованных томов допускается только при явном --initial-deployment и корпусе как единственном содержимом DATA_ROOT; обычный backup отказывает при пропавших томах. В инструкции добавлен обязательный проверенный backup после загрузки индекса и создания admin. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (16), rollback (3), shell guards, git diff --check; отдельно проверил создание, проверку и staging-restore архива корпуса с отсутствующими томами. Production VPS и публичный TLS в рамках ревью не проверялись.
Повторно проверил исправления на head 3b41d1f. Оба прежних замечания устранены: отсутствие именованных томов допускается только при явном `--initial-deployment` и корпусе как единственном содержимом DATA_ROOT; обычный backup отказывает при пропавших томах. В инструкции добавлен обязательный проверенный backup после загрузки индекса и создания admin. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (16), rollback (3), shell guards, `git diff --check`; отдельно проверил создание, проверку и staging-restore архива корпуса с отсутствующими томами. Production VPS и публичный TLS в рамках ревью не проверялись.
admin
merged commit a34f516de8 into main2026-10-11 20:22:23 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Что изменено
Проверки
Ограничения
Read-only SSH подтвердил, что на VPS у codex нет sudo, Docker и Git. Production bootstrap, установка helpers, backup/restore на реальных данных и публичный TLS не проверялись. Gitea CI для текущего head
9084fd4всё ещё Waiting to run; runner availability API требует владельца репозитория (403). Требуется ручное ревью; PR не объединён.Проверил PR независимо. Нужны исправления в процедурах первого развёртывания: сейчас описанный порядок нельзя выполнить с добавленным архиватором, а README production противоречит новой схеме доступа и резервного копирования. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (9), rollback (3), оба shell guard теста. На production запуск и восстановление не проверялись.
@@ -19,0 +27,4 @@the existing firewall rules and allow TCP `80` and `443` before Caddy starts.Caddy requests and renews the origin certificate automatically; useCloudflare Full (strict) only after HTTPS succeeds.5. Complete and verify the backup/restore setup before starting production.P2 — инструкция production устарела и противоречит этой же ветке. Здесь сказано, что проверенного backup-скрипта нет, хотя PR добавляет
infra/backup/archive.pyи процедуру вdocs/operations/BACKUP.md. Шаги ниже запускаютdocker composeбезsudoи используют.envв checkout, тогда как новая схема требует root-действий, не даётcodexдоступ к Docker и хранит env в/etc/akyldash/production.env. Обновите README до одного выполнимого порядка для владельца VPS.@@ -0,0 +31,4 @@existing SSH access. The Compose Caddy service obtains and renews its HTTPScertificate automatically; Cloudflare Full (strict) still needs owner-sideverification.5. Create and verify the first backup before starting services. Load andP1 — первый backup по этой инструкции невозможен.
infra/backup/archive.py:138–163требует, чтобы у всех сервисов уже существовали остановленные Compose-контейнеры. На чистом VPS до первогоup/createих нет, поэтомуcreateзавершается ошибкойCompose has no containers. Кроме того, backup до загрузки индекса и создания admin не покрывает эти данные. Добавьте выполнимый порядок для первоначального создания контейнеров/томов и отдельный проверенный backup после инициализации данных.Повторно проверил исправления на текущем head. Замечание про устаревший production README исправлено, новый архиватор корректно различает пустое хранилище и отсутствие контейнеров в покрытых тестами случаях. Первый запуск с предусмотренным инструкцией заранее загруженным корпусом всё ещё не имеет выполнимого безопасного порядка, поэтому оставляю Request changes. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (15), rollback (3), shell guard тесты и
git diff --check.@@ -59,0 +40,4 @@On a clean first deployment, `infra/backup/archive.py create` reports that nobackup is required when all persistent mounts are empty and no Composecontainers exist. Continue without a backup in that case. If any persistentdata exists, create and verify a backup before changing schema, images, orP1 — первый запуск с заранее загруженным корпусом по этой процедуре блокируется. Строки 24–25 требуют положить
minjust-normalizedвDATA_ROOTдо запуска. На новом VPS именованные тома Compose ещё не созданы;compose_sources()видит непустой bind mount и отсутствующие тома и выдаётpersistent Compose volumes are missing while data exists(infra/backup/archive.py:187–192). Это прямо зафиксировано новым тестомtest_existing_data_with_missing_named_volume_is_rejected_as_incomplete. Предложенное здесьcreate and verify a backupвыполнить невозможно, а пропускать backup при существующем корпусе инструкция запрещает. Нужен описанный и проверенный путь первоначального создания пустых томов и backup корпуса доupлибо другой безопасный порядок.@@ -0,0 +37,4 @@are no containers and all persistent mounts are empty. If any data exists,create and verify a backup before starting a new version. An uncertain orpartial state stops deployment. Then start services, load and validate theOpenSearch index, create the initial admin interactively, and verifyP2 — после инициализации данных процедура оставляет первый релиз без восстановимой точки. Для действительно пустого хоста backup пропускается, затем создаются индекс и admin, но шаги не останавливают сервисы и не создают проверенный backup новых SQLite/OpenSearch данных перед завершением первого развёртывания. Добавьте обязательный холодный backup и проверку restore после этой инициализации, прежде чем считать production готовым.
Проверил коммит
752266d. Ранее отмеченный случай с заранее загруженным корпусом теперь обрабатывается и покрыт тестом. Однако допуск отсутствующих томов создаёт риск неполного backup уже работавшей установки, а обязательный backup после первой инициализации индекса и admin всё ещё не добавлен. Поэтому оставляю Request changes. Локальные suites прошли: backend 37 (1 skip), Telegram 6, backup/restore 15, rollback 3; shell guards иgit diff --checkпрошли.@@ -63,0 +69,4 @@sudo docker compose --env-file /etc/akyldash/production.env -f deploy/production/compose.yaml exec backend python -m search.api --create-admin```The account database is `$DATA_ROOT/search-users.sqlite3` and is included inP2 — нет обязательного backup после первой инициализации. Порядок для полностью пустого хоста пропускает backup, затем создаёт индекс и admin, но не требует остановить сервисы, сделать и проверить архив получившихся SQLite/OpenSearch данных и пробное staged restore. В
docs/operations/DEPLOYMENT.mdпоследовательность заканчивается проверкой/reviewи записью image ID. Добавьте эту процедуру до признания первого развёртывания завершённым.@@ -0,0 +188,4 @@persistent_data = has_persistent_data(storage)if not containers and not persistent_data:raise ValueError("no backup is required for a clean first deployment; skip backup and start the initial stack")if containers and any(path is None for path in storage.values()):P1 — отсутствующий том после прежнего запуска теперь молча попадает в «проверенный» backup как пустой. Условие допускает
Noneдля любого named volume всякий раз, когда контейнеров нет, без признака именно первого развёртывания. Например, послеdocker compose downи потериakyldash_opensearch-dataоставшиеся SQLite/корпус вDATA_ROOTделаютpersistent_data=True; код создаст архив с пустымopensearch/и успешной SHA-проверкой вместо отказа из-за утраченного индекса. Новый тест подтверждает такое поведение для существующих данных и отсутствующего тома. Разрешайте пустые компоненты только при явно проверенном первом запуске; в остальных случаях прекращайте backup как неполный.Повторно проверил исправления на head
3b41d1f. Оба прежних замечания устранены: отсутствие именованных томов допускается только при явном--initial-deploymentи корпусе как единственном содержимом DATA_ROOT; обычный backup отказывает при пропавших томах. В инструкции добавлен обязательный проверенный backup после загрузки индекса и создания admin. Локально прошли backend (37, 1 skip), Telegram (6), backup/restore (16), rollback (3), shell guards,git diff --check; отдельно проверил создание, проверку и staging-restore архива корпуса с отсутствующими томами. Production VPS и публичный TLS в рамках ревью не проверялись.