# Deployment

## 1. Назначение документа

Этот документ фиксирует platform-agnostic модель деплоя проекта.

Он отвечает на вопросы:

- какие deployment artifacts должны быть в репозитории;
- как проект должен запускаться в production-like окружении;
- как routing должен работать через reverse proxy;
- что должен делать CD pipeline.

Документ не привязан к конкретному hypervisor, cloud provider, типу VM или домашней инфраструктуре.

## 2. Базовая модель деплоя

Для MVP принимается контейнерная модель:

- `admin-api`, `mcp-server` и `ui` упаковываются в контейнеры;
- `PostgreSQL` запускается как отдельный контейнер;
- сервисы запускаются через `docker-compose`;
- перед приложением находится reverse proxy;
- CI проверяет качество, CD доставляет и перезапускает сервисы.

## 3. Что должно быть в репозитории

В проекте должны существовать:

- `Dockerfile` для `apps/admin-api`;
- `Dockerfile` для `apps/mcp-server`;
- `Dockerfile` для `apps/ui` или отдельный production build flow для UI;
- `docker-compose.yml`;
- `.env.example`;
- `.github/workflows/ci.yml`;
- `.github/workflows/deploy.yml`;
- примеры reverse proxy конфигураций.

Рекомендуемое стартовое распределение портов:

- `ui` -> `3000`
- `admin-api` -> `3001`
- `mcp-server` -> `3002`

## 4. Production-like runtime состав

Минимальный набор сервисов:

- `ui`
- `admin-api`
- `mcp-server`
- `postgres`

Persistent data:

- volume для `postgres`;
- volume или bind mount для `artifact storage`.

## 5. Рекомендуемая routing model

Под одним доменом:

- `/` -> `ui`
- `/api/admin/` -> `admin-api`
- `/mcp/` -> `mcp-server`

Это позволяет:

- централизовать TLS;
- не плодить поддомены без необходимости;
- держать demo URL простым;
- использовать один reverse proxy для UI, API и MCP.

## 6. Требования к reverse proxy

Reverse proxy должен обеспечивать:

- TLS termination;
- редирект `HTTP -> HTTPS`;
- `X-Forwarded-*` headers;
- отключение buffering для MCP endpoint;
- увеличенные таймауты для долгих runtime-запросов;
- корректную маршрутизацию UI, admin API и MCP.

Особенно для `mcp-server` важно:

- `proxy_buffering off`;
- `proxy_request_buffering off`;
- длинный `proxy_read_timeout`.

## 7. Пример `nginx`

```nginx
server {
    listen 80;
    server_name example.com;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    client_max_body_size 25m;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_http_version 1.1;

    location / {
        proxy_pass http://ui:3000;
    }

    location /api/admin/ {
        proxy_pass http://admin-api:3001/;
    }

    location /mcp/ {
        proxy_pass http://mcp-server:3002/;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}
```

## 8. Пример `Traefik`

```yaml
services:
  ui:
    labels:
      - traefik.enable=true
      - traefik.http.routers.ui.rule=Host(`example.com`) && PathPrefix(`/`)
      - traefik.http.services.ui.loadbalancer.server.port=3000

  admin-api:
    labels:
      - traefik.enable=true
      - traefik.http.routers.admin.rule=Host(`example.com`) && PathPrefix(`/api/admin`)
      - traefik.http.services.admin.loadbalancer.server.port=3001

  mcp-server:
    labels:
      - traefik.enable=true
      - traefik.http.routers.mcp.rule=Host(`example.com`) && PathPrefix(`/mcp`)
      - traefik.http.services.mcp.loadbalancer.server.port=3002
```

Для production нужно отдельно проверить timeout behavior на MCP маршруте.

## 9. Требования к `docker-compose`

`docker-compose.yml` должен:

- поднимать `postgres`, `admin-api`, `mcp-server`, `ui`;
- использовать env vars вместо hardcoded config;
- иметь persistent storage для БД и artifacts;
- поддерживать настраиваемый bind host для published ports;
- публиковать `ui`, `admin-api` и `mcp-server` на loopback, если reverse proxy живет на том же хосте;
- публиковать `ui`, `admin-api` и `mcp-server` на LAN bind address или `0.0.0.0`, если reverse proxy вынесен на отдельный хост;
- не публиковать внутренние сервисы наружу без необходимости;
- содержать healthchecks.

Практически это должно быть управляемо через env вроде `CRANK_PUBLISH_HOST`:

- `127.0.0.1` для local reverse proxy на том же Linux host;
- конкретный внутренний IP или `0.0.0.0` для отдельного центрального reverse proxy в LAN.

## 10. Healthchecks

Нужны как минимум:

- `GET /health` для `admin-api`
- `GET /health` для `mcp-server`

CD не должен считать deployment успешным, пока эти endpoints не начали возвращать `200 OK`.

## 11. CI и CD

### 11.1. CI

CI должен выполнять:

- `cargo fmt --all --check`
- `cargo check --workspace`
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
- `cargo test --workspace --all-targets`

### 11.2. CD

CD для MVP должен:

- запускаться только после успешного `CI` на `main` или вручную;
- собирать production artifacts;
- доставлять deployment files на целевой Linux host;
- выполнять controlled restart;
- проверять health endpoints после запуска.

Минимальный flow:

1. push в `main`
2. успешный `CI`
3. build images или удаленная сборка на хосте
4. upload env и deployment files
5. `docker compose config -q`
6. `docker compose up -d`
7. healthcheck verification для `ui`, `admin-api`, `mcp-server`

Для MVP допустим deployment с удаленной сборкой на целевом Linux host через `ssh + rsync + docker compose up -d --build`, если не используется отдельный container registry.

## 12. Что нельзя забыть

- TLS и renewal сертификатов;
- backup для `postgres` и artifact storage;
- секреты вне репозитория;
- ограничение доступа к `admin-api`;
- rollback на предыдущий image tag или предыдущую compose revision.
