diff --git a/README.md b/README.md index 9ed4c2c..bb77e99 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ Production-контур для домена `rf4spotter.ru`, TLS, секреты Политика минимизации данных и ежедневная dry-run-first очистка описаны в [`docs/data-retention.md`](docs/data-retention.md). Host-side мониторинг контейнеров, readiness, диска, объёма PostgreSQL/MinIO, резервных копий и TLS описан в [`docs/production-monitoring.md`](docs/production-monitoring.md). + +Принятые границы стека, memory-cache, scheduler и хранилищ зафиксированы в [архитектурных решениях](docs/architecture-decisions.md). Порядок действий при заполнении диска, отказах PostgreSQL/MinIO, зависшем импорте, ошибке миграции и утечке секрета находится в [incident runbook](docs/incident-runbook.md). Ежедневный systemd timer создаёт проверяемую копию до retention-очистки, а production Compose ограничивает рост JSON-логов контейнеров. Фактическое состояние DNS/TLS домена и серверный чек-лист ведутся в [`docs/deployment-status.md`](docs/deployment-status.md). diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index fa83681..ab241c2 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -57,7 +57,7 @@ - [ ] **Q14 · Полная CSP.** Расширить текущую CSP (`frame-ancestors`, `base-uri`, `object-src`) до `default-src`, `script-src`, `style-src`, `img-src`, `connect-src` и `form-action`. Сначала инвентаризировать inline scripts/styles Astro, затем внедрить nonce/hash или безопасное вынесение; проверить report/admin/OG без ослабления до произвольных внешних origin. - [ ] **Q15 · Частичная деградация главной.** Разделить получение activity, community signals и справочников так, чтобы отказ одного источника не превращал всю главную в общий 503. Для SSR не добавлять искусственный client-side loading/optimistic UI; показывать независимые `StatePanel` и корректный HTTP/cache статус. - [x] **Q16 · Контракт OpenAPI.** `apps/api/openapi.json` детерминированно генерируется из FastAPI; CI проверяет его актуальность после backend suite. Изменение artifact обязательно рассматривается вместе с реализацией, а ручное редактирование не используется. -- [ ] **Q17 · Эксплуатационные документы.** Добавить короткие ADR по Astro/FastAPI/PostgreSQL, локальному cache и стратегии scheduler, а также incident runbook для заполнения диска/PostgreSQL, отказа MinIO, зависших импортов, ошибок миграции и компрометации секретов. +- [x] **Q17 · Эксплуатационные документы.** Зафиксированы ADR по Astro/FastAPI/PostgreSQL, локальному cache, scheduler/cooldown и разделению PostgreSQL/MinIO. Incident runbook покрывает заполнение диска, отказ PostgreSQL/MinIO, зависшие импорты, ошибки миграций, компрометацию секретов и критерии закрытия без опасных reset/recreate операций. - [ ] **Q18 · Минимальная observability.** До открытой альфы определить дешёвые метрики request count/latency/error rate, глубины moderation/staging и возраста последнего успешного импорта. Формат и exporter выбрать после выбора мониторинга сервера; полноценный tracing не внедрять без подтверждённой потребности. ### Административная панель diff --git a/docs/architecture-decisions.md b/docs/architecture-decisions.md new file mode 100644 index 0000000..d887e0a --- /dev/null +++ b/docs/architecture-decisions.md @@ -0,0 +1,17 @@ +# Архитектурные решения + +## ADR-001 · Astro + FastAPI + PostgreSQL + +Статус: принято. Astro отвечает за быстрый SSR-интерфейс и SEO, FastAPI — за типизированный API и фоновые операции, PostgreSQL — за канонические данные, блокировки и миграции. Next.js/Vinext не вводятся: второй web-runtime усложнит сборку, кэш и эксплуатацию без пользы для текущего продукта. + +## ADR-002 · Локальный cache API + +Статус: принято для одного API-процесса. Публичная активность хранится в ограниченном memory-cache 20 секунд и инвалидируется после изменений в том же процессе. Данные scheduler могут появиться с задержкой до TTL. Redis нужен только при нескольких API-процессах или измеренной проблеме; до этого он создаёт лишнюю stateful-зависимость. + +## ADR-003 · Отдельный scheduler и общий cooldown + +Статус: принято. Scheduler отделён от HTTP API, а попытка импорта резервируется в PostgreSQL до сетевого запроса. Endpoint одной площадки делят минимальный интервал 30 минут; ошибка также расходует окно, повторные ошибки увеличивают паузу до 24 часов. Ручной production-запуск проходит через тот же журнал, чтобы не обходить лимит. + +## ADR-004 · PostgreSQL и MinIO как разные контуры данных + +Статус: принято. Метаданные и moderation state хранятся в PostgreSQL; бинарные скриншоты — в MinIO под отдельными минимальными credentials. База не содержит большие изображения, а API не получает root-доступ к object storage. Backup считается полноценным только при согласованном сохранении обоих контуров и проверенном restore. diff --git a/docs/incident-runbook.md b/docs/incident-runbook.md new file mode 100644 index 0000000..e6c97fe --- /dev/null +++ b/docs/incident-runbook.md @@ -0,0 +1,45 @@ +# Incident runbook RF4 Spotter + +Сначала зафиксировать время, revision из `/api/v1/admin/diagnostics`, симптомы и последние изменения. Не удалять volumes и не запускать миграции повторно вслепую. Если есть риск порчи или утечки, включить maintenance mode по `deploy/README.md`. + +## Заполнение диска + +1. Проверить host disk, размеры PostgreSQL/MinIO и возраст backup через `deploy/monitor.sh`. +2. Остановить scheduler, чтобы не росли импорт и логи; публичное чтение оставлять только если база стабильна. +3. Освободить место вне volumes: старые build-artifacts и журналы согласно системной retention. Не удалять файлы MinIO или PostgreSQL вручную. +4. Выполнить штатный retention dry-run, затем apply только после проверки отчёта. После восстановления места проверить `/ready` и создать свежий backup. + +## PostgreSQL недоступен + +1. Проверить контейнер, healthcheck, свободное место и последние логи без вывода секретов. +2. Не выполнять автоматический reset/recreate. При повреждении развернуть отдельный чистый контур и использовать `deploy/restore.sh` с последним проверенным backup. +3. После запуска сверить Alembic head, `/ready`, счётчики diagnostics и контрольные данные. + +## MinIO недоступен + +1. Проверить health, диск и доступ app-пользователя только к целевому bucket. +2. Отключить загрузку новых скриншотов или включить maintenance; текстовые заявки не считать потерянными. +3. После восстановления проверить stat bucket, отрицательную проверку глобального list и чтение одного тестового объекта подписанной ссылкой. + +## Зависший импорт + +1. Проверить import run, advisory lock, время старта и процесс scheduler. Не запускать второй импорт того же источника. +2. Если процесса уже нет, сохранить диагностику и пометить run failed штатным административным способом; не менять опубликованные записи. +3. Следующий запуск делать только после site-wide cooldown. При повторных ошибках оставить backoff и проверить DOM-контракт на fixture. + +## Ошибка миграции + +1. Остановить rollout; не запускать API новой версии поверх неизвестной схемы. +2. Сохранить логи migrate и backup. Определить, транзакционна ли упавшая ревизия и какой `alembic current` фактически записан. +3. Предпочесть исправленную forward migration. Rollback приложения допустим, только если старая версия совместима с текущей схемой; восстановление БД — по документированной release-процедуре. + +## Компрометация секрета + +1. Немедленно включить maintenance и отозвать затронутый секрет: admin token, Basic Auth, rate-limit HMAC, app S3 или root MinIO. +2. Создать новый уникальный секрет, обновить `.env.production` вне Git и перезапустить только зависимые сервисы. Для MinIO сначала выпустить нового app-пользователя, проверить policy, затем удалить старого. +3. Проверить moderation history, auth attempts, object operations и deploy-доступ за предполагаемый период без публикации персональных данных. +4. Если мог раскрыться `RATE_LIMIT_SECRET`, учитывать, что сменятся HMAC-идентификаторы клиентов. Зафиксировать инцидент и время ротации. + +## Закрытие инцидента + +`/health` и `/ready` успешны, monitor не выдаёт предупреждений, последняя миграция и backup проверены, причина и принятые меры записаны. После критического инцидента обязателен отдельный restore drill и короткое обновление этого runbook.