---
type: infra-requirements
work_item_id: ET-013
title: "Инфраструктурные требования — ET-013: Zoom-aware paint для terrain-слоёв на z9-z11"
version: 1
status: approved
created_at: 2026-06-04
authors:
  - "agent:architect"
---

# Инфраструктурные требования — ET-013

## 1. Резюме

ET-013 — **frontend paint-калибровка**. Меняются два файла исходного
кода (`src/web/app.js`, `src/web/index.html`) + добавляются тесты.
Инфраструктура **не меняется**:

- 0 новых docker-сервисов;
- 0 изменений в `Dockerfile`;
- 0 изменений в `docker-compose.yml`;
- 0 новых файлов БД, миграций, индексов;
- 0 новых cron-записей;
- 0 новых env / секретов / API-ключей;
- 0 новых исходящих HTTPS-соединений;
- 0 новых портов;
- 0 изменений в nginx (тайлы рельефа отдаются с тех же путей
  `/enduro/terrain/{layer}/{z}/{x}/{y}.png`);
- 0 изменений в backend (`src/api/main.py:terrain_tile` без правок).

Эскалация: **minor change** (см. ADR-017 §«Классификация изменения»).

## 2. Контейнеры и сервисы

| Аспект                                       | Требование                                                                                                                                                       |
|----------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Новый сервис                                 | **Нет**                                                                                                                                                          |
| Изменения `Dockerfile`                       | **Нет**                                                                                                                                                          |
| Изменения `docker-compose.yml`               | **Нет**                                                                                                                                                          |
| Перезапуск `app` после деплоя                | Нужен — `docker compose up -d --no-deps app` (≈ 5 сек простоя). Подхватывает обновлённые `src/web/app.js` и `src/web/index.html` (отдаются как статика из контейнера) |
| Перезапуск `gps-collector`                   | Не нужен (не затронут)                                                                                                                                           |
| Очистка серверных кэшей                      | Не требуется (backend не меняется; `/terrain/*` endpoint и `Cache-Control: max-age=31536000, immutable` без изменений)                                          |
| Очистка клиентских кэшей                     | Не требуется как часть деплоя, но пользователю при первой загрузке после деплоя браузер дёрнет свежий `app.js` (cache-busting через nginx if-modified-since)      |

### 2.1 Зависимости между сервисами

Без изменений vs PH-6 / ET-007:

- `app` → файлы `/app/data/terrain/{hillshade,tri,hypso}/{z}/{x}/{y}.png`
  (read-only при отдаче клиенту).
- `nginx (host)` → `app:8000` через docker-network bridge.

## 3. Сеть

| Аспект                         | Требование                                                                                                                                                  |
|--------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Новые входящие порты           | **Нет**                                                                                                                                                     |
| Изменения nginx                | **Нет** (`location /enduro/terrain/` без правок; новые комбинации `(z, x, y)` для z=9 — просто другие значения существующего path-параметра)                |
| nginx gzip для PNG             | Не применяется (PNG уже сжат). Без изменений vs PH-6                                                                                                        |
| Кэш-заголовки на `/terrain/*`  | Без изменений: `Cache-Control: public, max-age=31536000, immutable` (см. `src/api/main.py:1252`). Браузерный кэш + nginx-кэш агрессивно поглощают повторы   |
| Новые исходящие соединения     | **Нет** — никаких внешних API не дёргается, всё локально                                                                                                    |
| CORS                           | Без изменений; `/terrain/*` отдаётся в том же origin, что и `index.html`                                                                                    |
| HTTPS / TLS                    | Без изменений — nginx с Let's Encrypt сертификатом DuckDNS                                                                                                  |

### 3.1 Ingress / Egress — оценка дельты

Изменения сетевого паттерна (BRD M-10, NFR-03):

- **Hillshade**: UI-минзум понижается с 10 до 9 → пользователь видит
  слой на одной zoom-ступени раньше. Один тайл z9 == 4 тайла z10 по
  покрытию территории, поэтому при «активной zoom-сессии» z=8→z=12
  с включённым hillshade добавляется ≤ 1 zoom-ступень тайлов.
- **TRI**: minzoom источника не меняется (5), opacity меняется только
  для уже-запрашиваемых тайлов. Дельта запросов **0**.
- Итого: при типичной сессии «10 зумов между z8 и z12 с обоими слоями»
  объём PNG растёт **≤ 35%** (BRD M-10, AC-21).

Размер одного PNG-тайла рельефа (terrain) ≈ 8-30 KB (без gzip — PNG
уже сжат). На сессию: было ~60 тайлов × 20 KB = 1.2 MB, станет
~80 тайлов × 20 KB = 1.6 MB. Дельта на пользователя: ~0.4 MB.

При 10 одновременных пользователях на mva154 — пик ≈ 4 MB/сек
дополнительного uplink, мизер по сравнению с uplink сервера
(≥ 100 Mbps по DuckDNS).

Кэш браузера (`immutable, max-age=31536000`) поглощает 2-й и
последующие визиты целиком.

### 3.2 Rate-limit на `/terrain/*`

**Не вводим в этой итерации.** PNG-тайлы — статика с агрессивным
кэшем; DDoS-стоимость низкая (sendfile из ФС без вычислений). Если в
проде обнаружится скан z=9-z=14 grid'а — добавляется отдельным
DevOps-task'ом, не в ET-013.

## 4. Серверные ресурсы

| Аспект                  | Требование                                                                                                                                                                                |
|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CPU `app`               | Без изменений по архитектуре. Раздача PNG — `FileResponse` (sendfile, zero-copy через ядро), CPU-cost пренебрежимый. Рост запросов до +35% даёт +0.5% CPU на сервере при пике сессий       |
| RAM `app`               | Без изменений. PNG не буферизуются в памяти; sendfile из файловой системы                                                                                                                  |
| Disk `app`              | Без изменений. Тайлы рельефа лежат в `/home/slin/enduro-trails/data/terrain/{hillshade,tri,hypso}/{z}/{x}/{y}.png` (объём по PH-6 baseline). Никаких новых файлов / volume                |
| CPU `gps-collector`     | Без изменений (не затронут)                                                                                                                                                                |
| RAM `gps-collector`     | Без изменений                                                                                                                                                                              |
| Disk `gps-collector`    | Без изменений                                                                                                                                                                              |

### 4.1 Размер тайлов рельефа на диске

**Не меняется.** ET-013 не перегенерирует тайлы; используются
существующие нарезки z8-z14 из PH-6. Если pre-deploy smoke
(см. §6.2 шаг 1) обнаружит отсутствие тайлов z9-z11 — задача
останавливается, открывается PH-6 follow-up на догенерацию
(BRD R-11, AC-19).

## 5. Конфигурация и секреты

| Аспект                              | Требование                                                                                                                                  |
|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| Новые env-переменные                | **Нет**                                                                                                                                     |
| Новые секреты                       | **Нет**                                                                                                                                     |
| Новые API-ключи                     | **Нет**                                                                                                                                     |
| Изменения `config/*.yaml`           | **Нет**                                                                                                                                     |
| Изменения runtime config            | **Нет** — `HILLSHADE_PAINT` и `TRI_PAINT` — JS-константы, живут в коде и меняются коммитом (BRD §6 q&a, ADR-017 §M)                          |
| Изменения `style.json` / `style-dark.json` | **Нет** — растровые terrain-слои добавляются динамически из JS, в стилях не описаны                                                  |

## 6. Деплой

### 6.1 Среды

- **dev (локально)**: `make dev` (docker compose up `app`). Достаточно
  `git pull && make dev` для смены поведения.
- **test (mva154)**: `https://openclaw.mva154.duckdns.org/enduro/`.
  CI/CD — Gitea Actions; деплой через `make deploy-test` или ручной
  SSH + `docker compose up -d --no-deps --build app` (см. §6.2).
- **prod** — пока не задействован; ET-013 деплоится только в test.

### 6.2 Процедура деплоя в test

Последовательность шагов (REQ-F-20 в TRZ §3):

1. **Pre-deploy smoke**: проверить наличие тайлов z9-z11 на test-среде:
   ```bash
   curl -sI 'https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/9/308/158.png'  | head -1
   curl -sI 'https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/10/617/317.png' | head -1
   curl -sI 'https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/11/1234/635.png' | head -1
   ```
   Ожидается `HTTP/1.1 200 OK` на все три. Если хотя бы один 404 —
   merge приостанавливается (AC-19), открывается PH-6 follow-up на
   догенерацию тайлов.
2. **Сборка образа**: `docker compose build app` на mva154 (после `git pull`).
3. **Перезапуск `app`**: `docker compose up -d --no-deps app`.
4. **Post-deploy smoke**:
   ```bash
   # Проверка статики app.js обновился
   curl -s 'https://openclaw.mva154.duckdns.org/enduro/app.js' | grep -c 'HILLSHADE_PAINT'
   # Ожидается ≥ 1
   ```
5. **Ручная валидация AC-03..AC-12** через DevTools:
   - открыть карту, центр над Окой/югом Москвы (`[37.6, 54.5]`);
   - `window._map.setZoom(9)` — кнопка «Тени рельефа» активна, hint скрыт;
   - включить «Тени рельефа» и «Перепады»;
   - скриншоты на z9/z10/z11/z14 → визуальная приёмка AC-07..AC-10;
   - переключить тему `theme-dark` → проверить AC-11;
   - переключить подложку `#base-btn-satellite` → проверить AC-12.
6. **Запись результатов в `13-test-report.md` и `14-deploy-log.md`**.

### 6.3 Rollback

В случае проблем (например, AC-11 «hillshade сливается с dark-темой»,
без возможности быстрой donastройки stops):

1. **Frontend rollback**: `git revert <commit>` + `docker compose up -d --no-deps --build app`.
2. **Cache invalidation**: не требуется (backend не меняется, browser
   cache на статике `app.js` инвалидируется по if-modified-since
   автоматически).

RTO: ≤ 5 минут (один `docker compose up -d --no-deps app`).
RPO: 0 — никаких изменений в БД, никаких данных не теряется.

### 6.4 CI/CD-гейты

- `make lint` (ruff + eslint) — должен быть зелёным (AC-18).
- `make test` (pytest unit + integration) — зелёный (AC-15..AC-17).
- `pytest tests/integration/test_terrain_z9_tiles.py` — c
  `@pytest.mark.skipif` для CI без данных (AC-16), не блокирует
  merge.

## 7. Observability / Логирование

| Аспект                       | Требование                                                                                                                                            |
|------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| Новые лог-сообщения          | **Нет** (NFR-06 в TRZ §4)                                                                                                                              |
| Существующие лог-сообщения   | `uvicorn.access` логирует все запросы к `/terrain/*` с длиной ответа — этого достаточно для мониторинга дельты трафика после деплоя                  |
| Метрики / Prometheus         | Не вводим в MVP                                                                                                                                       |
| Health-endpoint              | `GET /api/health` (если есть) — без изменений                                                                                                          |

### 7.1 Что мониторить после деплоя

В `nginx access.log` на mva154 (вручную, без алёртов) — первая неделя:

- **Запросы к `/terrain/hillshade/9/*/*.png`**: должны появиться
  (раньше клиент их не дёргал). Если 404 — `data/terrain/hillshade/9/`
  отсутствует, инцидент (BRD R-11).
- **Объём ответов**: ≤ +35% к baseline на терминальную пользовательскую
  сессию (BRD M-10, AC-21).
- **Status codes**: только 200/304 (304 от if-modified-since). Никаких
  500/502 быть не должно.

## 8. Резервное копирование / Disaster recovery

| Аспект                       | Требование                                                                                          |
|------------------------------|-----------------------------------------------------------------------------------------------------|
| Backup БД                    | Без изменений vs ET-008/PH-6 (ET-013 не трогает БД)                                                  |
| Backup тайлов рельефа        | Без изменений vs PH-6. Регенерируемы из SRTM при необходимости                                       |
| Время восстановления (RTO)   | ≤ 5 минут (rollback контейнера, см. §6.3)                                                            |
| Точка восстановления (RPO)   | 0 — никаких данных не теряется                                                                       |

## 9. Безопасность

| Аспект                              | Требование                                                                                                              |
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| Auth / Authorization                | Без изменений (NFR-05 в TRZ §4). `/terrain/*` — публичный (как и был)                                                   |
| Валидация входных данных            | Без изменений; existing валидация `(z, x, y)` в `terrain_tile` уже корректно принимает любые валидные z                  |
| CSP                                 | Без изменений                                                                                                            |
| Rate-limit                          | Не вводим в MVP (см. §3.2)                                                                                               |
| TLS                                 | Без изменений — nginx с Let's Encrypt сертификатом DuckDNS                                                               |

## 10. Совместимость

| Аспект                                     | Требование                                                                                                                                                                                                                |
|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| API контракт `/terrain/*`                  | Не меняется (REQ-F-18). Любые клиенты (старые tab'ы со старым `app.js`) продолжают работать; они просто не дёргают z=9 hillshade                                                                                          |
| MapLibre GL JS совместимость                | MapLibre 4.7.0 (`index.html:10`) поддерживает `interpolate` для `raster-opacity` и `raster-contrast`. `raster-resampling` не поддерживает `interpolate` — поэтому глобально `'nearest'` (см. ADR-017 §R)                  |
| Совместимость с PH-6 stack                  | Никаких изменений; калибровка идёт поверх существующих PH-6 тайлов                                                                                                                                                         |
| Совместимость с ET-007 (Спутник)            | AC-12 проверяет визуально. В случае проблем — открывается ADR-018 (theme-specific paint)                                                                                                                                  |
| Совместимость с ET-005 (units), ET-006 (GPX), ET-008 (public tracks) | Без изменений; ET-013 трогает только terrain-слои                                                                                                                                              |
| Совместимость с OSRM                        | Не затронуто (роутинг работает с OSRM-графом независимо)                                                                                                                                                                   |
| localStorage migration                      | Не нужно (REQ-F-17). Существующие ключи `terrain-hillshade`, `terrain-tri` — без изменений. Пользователи с включённым hillshade автоматически увидят слой на z9 при следующей загрузке                                    |

## 11. Связанные документы

- `01-brd.md` §3 (F-01..F-14), §6 (Зависимости, инфра), AC §AC-19 (pre-deploy check)
- `02-trz.md` §3 REQ-F-20 Деплой и валидация, §4 NFR
- `06-adr/ADR-017-zoom-aware-terrain-paint.md` §«Классификация изменения», §«Последствия»
- `08-data-requirements.md` (этот пакет)
- `10-tech-risks.md` (этот пакет)
- `docs/work-items/ET-012/07-infra-requirements.md` — образец «zero-infra» work-item (наследие)
- `docs/work-items/ET-011/07-infra-requirements.md` — образец «zero-infra» work-item (наследие)
