# Environment matrix и границы доступа

## Паспорт документа

- Статус документа: working reference
- Актуально на: 2 апреля 2026 года
- Владелец: backend/platform-команда
- Пересмотр: при изменении env-модели, secret boundaries, сервисов или ownership-модели
- Область применения: production и local environment-схема для `qadam-core`, `qadam-web` и `qadam-roadmap`
- Связанные документы:
  - [Runbook эксплуатации и деплоя](./deployment-runbook.md)
  - [Ownership model](../governance/ownership-model.md)
  - [Текущее состояние](../project/current-state.md)

## Цель документа

Этот документ фиксирует, где живут env-переменные, какие сервисы их используют и кто имеет право их менять. Это не список секретных значений. Источником истины здесь являются классы переменных, их владельцы и границы доступа.

## 1. Источники env по сервисам

| Сервис | Runtime | Источник env | Владелец |
|-------|---------|--------------|----------|
| `qadam-api` | rollback-only `/data/qadam-core/apps/api/dist/main.js` | `/etc/qadam/qadam.env` | backend/platform-команда |
| `qadam-api-container` | `docker compose` runtime для registry image `api` | `/etc/qadam/qadam-api-runtime.env` + `/etc/qadam/qadam.env` | backend/platform-команда |
| `qadam-web` | `/data/qadam-web/apps/web/.next/standalone/apps/web/server.js` | `/etc/qadam/qadam.env` + systemd `PORT=3000`, `HOSTNAME=127.0.0.1` | frontend-команда по app logic, backend/platform по production env |
| `qadam-web-runtime` | `docker compose` shadow runtime для registry image product web | `/etc/qadam/qadam-web-runtime.env` + shell/runtime vars `QADAM_WEB_*` | frontend-команда по app logic, backend/platform по runtime contour |
| `qadam-roadmap` | `/data/qadam-web/apps/roadmap/.next/standalone/apps/roadmap/server.js` | `/etc/qadam/qadam-roadmap.env` | backend/platform-команда |
| `qadam-core-runner` | `/opt/act_runner/qadam-core/act_runner daemon` | systemd unit + `/opt/act_runner/qadam-core/.runner` | backend/platform-команда |
| `qadam-monitor` | `/data/qadam-core/scripts/monitor-runtime.mjs` | `/etc/qadam/qadam.env` + `/etc/qadam/qadam-monitor.env` | backend/platform-команда |
| `qadam-backup` | `/data/qadam-core/scripts/backup-runtime.mjs` | `/etc/qadam/qadam.env` + `/etc/qadam/qadam-backup.env` | backend/platform-команда |

## 2. Канонические production env-группы

### Backend API (`/etc/qadam/qadam.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `NODE_ENV` | обязательна | нет | режим runtime |
| `PORT` | обязательна | нет | локальный порт API, сейчас `5001` |
| `HOSTNAME` | обязательна | нет | bind host, сейчас loopback |
| `DATABASE_URL` | обязательна | да | подключение к production PostgreSQL |
| `REDIS_URL` | условно обязательна | да | Redis для refresh/session infrastructure |
| `JWT_SECRET` | обязательна | да | подпись access/refresh token |
| `CORS_ORIGIN` | обязательна | нет | browser origin для web |
| `PUBLIC_WEB_BASE_URL` | опциональна | нет | canonical public base URL для backend-generated SEO metadata; если не задан, backend выводит его из `NEXT_PUBLIC_API_URL` |
| `UPLOADS_DIR` | условно обязательна | нет | локальный storage для image uploads при `OBJECT_STORAGE_DRIVER=local` |
| `OBJECT_STORAGE_DRIVER` | условно обязательна | нет | storage driver для product media: `local` или `s3` |
| `S3_ENDPOINT` | условно обязательна | нет | endpoint S3-совместимого storage при `OBJECT_STORAGE_DRIVER=s3` |
| `S3_REGION` | условно обязательна | нет | region/object storage region |
| `S3_BUCKET` | условно обязательна | нет | bucket для product media |
| `S3_ACCESS_KEY_ID` | условно обязательна | да | access key для object storage |
| `S3_SECRET_ACCESS_KEY` | условно обязательна | да | secret key для object storage |
| `S3_PUBLIC_BASE_URL` | опциональна | нет | CDN/public base URL, если раздача идёт не напрямую через bucket endpoint |
| `S3_FORCE_PATH_STYLE` | опциональна | нет | режим совместимости для MinIO и части S3-compatible провайдеров |
| `S3_OBJECT_ACL` | опциональна | нет | canned ACL для новых объектов; для текущего Spaces-контура нужен `public-read` |
| `AXIOM_TOKEN` | опциональна | да | внешняя отправка логов |
| `AXIOM_DATASET` | опциональна | нет | dataset для Axiom |
| `TELEGRAM_BOT_TOKEN` | опциональна | да | Telegram integration для operational alerts и мониторинга |
| `TELEGRAM_ALERT_CHAT_ID` | опциональна | нет | chat/channel ID для operational alerts и health-уведомлений |
| `SELLER_TELEGRAM_BOT_TOKEN` | опциональна | да | отдельный bot token для seller-facing Telegram notifications и onboarding webhook |
| `SELLER_TELEGRAM_BOT_USERNAME` | опциональна | нет | username seller-facing бота для UX/handoff (`@qadam_notify_bot` и т.п.) |
| `SELLER_TELEGRAM_WEBHOOK_SECRET` | опциональна | да | secret token, который Telegram присылает в `X-Telegram-Bot-Api-Secret-Token` для seller webhook |
| `SMTP_HOST` | опциональна | нет | SMTP host для seller email fallback |
| `SMTP_PORT` | опциональна | нет | SMTP port для seller email fallback |
| `SMTP_SECURE` | опциональна | нет | включает SMTPS/TLS режим (`true`/`false`) |
| `SMTP_USER` | опциональна | да | SMTP username, если transport требует auth |
| `SMTP_PASS` | опциональна | да | SMTP password, если transport требует auth |
| `SMTP_FROM` | опциональна | нет | sender/from для seller email notifications |
| `SMTP_REPLY_TO` | опциональна | нет | reply-to адрес для seller email notifications |
| `SELLER_STATUS_CHANGE_NOTIFY_STATUSES` | опциональна | нет | CSV-список lead statuses, по которым backend шлёт owner-facing status-change notifications при staff-driven update; по умолчанию `CONTACTED,ENROLLED,REJECTED` |

### API container runtime (`/etc/qadam/qadam-api-runtime.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `QADAM_API_IMAGE` | обязательна | нет | registry image tag для production API container runtime |
| `QADAM_API_PORT` | обязательна | нет | loopback-порт container runtime, сейчас `5002` |
| `QADAM_API_HOSTNAME` | обязательна | нет | bind host для container runtime, сейчас `127.0.0.1` |
| `QADAM_API_ENV_FILE` | обязательна | нет | env-файл, который пробрасывается в контейнер, сейчас `/etc/qadam/qadam.env` |
| `QADAM_API_COMPOSE_PROJECT` | обязательна | нет | compose project name для API runtime |

### Product web (`/etc/qadam/qadam.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `NEXT_PUBLIC_API_URL` | обязательна | нет | browser URL API |
| `API_URL` | обязательна | нет | server-side URL API, в текущем production host runtime должен указывать на `http://127.0.0.1:5002/api/v1` |
| `PORT` | задаётся systemd | нет | локальный порт web, сейчас `3000` |
| `HOSTNAME` | задаётся systemd | нет | bind host, сейчас loopback |

### Product web shadow/container runtime (`/etc/qadam/qadam-web-runtime.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `NODE_ENV` | обязательна | нет | режим runtime, сейчас `production` |
| `API_URL` | обязательна | нет | server-side URL API внутри контейнера; в текущем shadow runtime должен указывать на `http://host.docker.internal:5002/api/v1` |
| `NEXT_PUBLIC_API_URL` | обязательна | нет | browser URL API |

Дополнительные shell/runtime переменные этого контура задаются через `deploy/env/web-runtime.env.example` или через одноразовый shell-export:

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `QADAM_WEB_IMAGE` | обязательна | нет | registry image tag для product web runtime |
| `QADAM_WEB_PORT` | обязательна | нет | loopback-порт shadow runtime, сейчас `3002` |
| `QADAM_WEB_BIND_HOST` | обязательна | нет | bind host shadow runtime, сейчас `127.0.0.1` |
| `QADAM_WEB_ENV_FILE` | обязательна | нет | env-файл, который пробрасывается в контейнер, сейчас `/etc/qadam/qadam-web-runtime.env` |
| `QADAM_WEB_COMPOSE_PROJECT` | обязательна | нет | compose project name для product web runtime |

### Roadmap service (`/etc/qadam/qadam-roadmap.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `PORT` | обязательна | нет | локальный порт roadmap, сейчас `3001` |
| `HOSTNAME` | обязательна | нет | bind host, сейчас loopback |
| `QADAM_PROJECT_ROOT` | обязательна | нет | checkout канонических docs, сейчас `/data/qadam-core` |
| `QADAM_ROADMAP_STORAGE_DIR` | обязательна | нет | uploads/comments storage roadmap |

### Gitea Actions runner (`/opt/act_runner/qadam-core/.runner`)

| Переменная / источник | Обязательность | Секрет | Назначение |
|----------------------|----------------|--------|------------|
| `.runner` | обязательна | да | регистрационное состояние runner и привязка к `eldar/qadam-core` |
| `qadam-core-runner.service` | обязательна | нет | системный запуск `act_runner daemon` |

### External runtime monitor (`/etc/qadam/qadam-monitor.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `QADAM_MONITOR_PRODUCT_URL` | обязательна | нет | публичный URL product web для uptime-check |
| `QADAM_MONITOR_API_READY_URL` | обязательна | нет | readiness URL API для внешней проверки |
| `QADAM_MONITOR_ROADMAP_URL` | обязательна | нет | публичный URL roadmap-сервиса |
| `QADAM_MONITOR_ROADMAP_BASIC_USER` | обязательна | нет | basic auth login для roadmap uptime-check |
| `QADAM_MONITOR_ROADMAP_BASIC_PASS` | обязательна | да | basic auth password для roadmap uptime-check |
| `QADAM_MONITOR_TIMEOUT_MS` | опциональна | нет | timeout HTTP-check'ов |
| `QADAM_MONITOR_TLS_WARNING_DAYS` | опциональна | нет | порог warning по сроку жизни TLS-сертификата |
| `QADAM_MONITOR_STATE_FILE` | обязательна | нет | state file для dedupe/recovery логики алертов |

### Automated backup (`/etc/qadam/qadam-backup.env`)

| Переменная | Обязательность | Секрет | Назначение |
|-----------|----------------|--------|------------|
| `QADAM_BACKUP_ROOT_DIR` | обязательна | нет | локальный root для snapshot-каталогов |
| `QADAM_BACKUP_TMP_DIR` | обязательна | нет | временный каталог для сборки off-host archive |
| `QADAM_BACKUP_LOCAL_RETENTION_DAYS` | обязательна | нет | срок хранения локальных snapshot-каталогов |
| `QADAM_BACKUP_OFFSITE_ENABLED` | обязательна | нет | включает off-host upload в S3-compatible storage |
| `QADAM_BACKUP_OFFSITE_BUCKET` | опциональна | нет | bucket для off-host archive; по умолчанию берётся из `S3_BUCKET` |
| `QADAM_BACKUP_OFFSITE_PREFIX` | обязательна | нет | префикс object key для backup archive |
| `QADAM_BACKUP_OFFSITE_RETENTION_DAYS` | обязательна | нет | срок хранения off-host archive |
| `QADAM_BACKUP_NOTIFY_ON_SUCCESS` | опциональна | нет | разрешает обычные success-уведомления в Telegram; fail и recovery отправляются автоматически по смене состояния |
| `QADAM_BACKUP_STATE_FILE` | обязательна | нет | state file последнего backup-run |
| `QADAM_BACKUP_PG_DUMP_BIN` | обязательна | нет | путь до `pg_dump` |
| `QADAM_BACKUP_PG_RESTORE_BIN` | обязательна | нет | путь до `pg_restore` |

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

- production secrets;
- содержимое `/etc/qadam/*.env`;
- содержимое `/opt/act_runner/qadam-core/.runner`;
- пароли БД, JWT secrets, bot tokens, внешние integration keys;
- `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` и любые другие object storage credentials;
- ручные `.env` с боевыми значениями;
- Dockerfile, image layers, committed compose manifests и build args с боевыми секретами;
- данные из `/var/lib/qadam-core` и `/var/lib/qadam-roadmap`.

## 4. Границы изменения env

### Backend/platform-команда меняет

- production env-файлы в `/etc/qadam`;
- `systemd` units;
- `nginx` конфигурацию;
- `DATABASE_URL`, `REDIS_URL`, `JWT_SECRET`, `UPLOADS_DIR`, `OBJECT_STORAGE_DRIVER`, `S3_*`, `QADAM_PROJECT_ROOT`;
- `PUBLIC_WEB_BASE_URL`, если backend начинает отдавать canonical/SEO URLs;
- TLS, домены и bind policy.
- `qadam-monitor.service`, `qadam-monitor.timer` и `/etc/qadam/qadam-monitor.env`.
- `qadam-backup.service`, `qadam-backup.timer` и `/etc/qadam/qadam-backup.env`.

### Frontend-команда меняет

- локальные dev `.env` в `qadam-web`;
- frontend-only env для локальной разработки и preview-контуров;
- не меняет production secrets напрямую.

### Совместное согласование требуется

- при изменении `API_URL` / `NEXT_PUBLIC_API_URL`;
- при изменении `PUBLIC_WEB_BASE_URL` или любой backend-generated canonical/SEO URL;
- при изменении auth-контуров или cookie-модели;
- при появлении нового обязательного env для web-flow;
- при изменении OpenAPI source/workflow;
- при смене режима `OBJECT_STORAGE_DRIVER`, bucket/public URL или политики доступа product media.

## 5. Принцип для docker и object storage secrets

- Production credentials не вшиваются в `Dockerfile`, image layers или в git-managed manifests.
- Для текущего mixed runtime secrets поставляются через `/etc/qadam/*.env`.
- Для действующего docker/image-based runtime API и для будущего image-based runtime остальных сервисов те же secrets должны поставляться через runtime env/secrets механизмы, а не через `docker build`.
- `Dockerfile` может содержать только несекретные build-time параметры.

## 6. Local development matrix

### `qadam-core`

- файл: `qadam-core/.env`
- владелец локальной конфигурации: backend/platform-команда
- типовая связка:
  - `PORT=5001`
  - локальная PostgreSQL
  - локальный Redis
  - `CORS_ORIGIN=http://localhost:3000`

### `qadam-web`

- файл: `qadam-web/.env`
- владелец локальной конфигурации: frontend-команда
- типовая связка:
  - `NEXT_PUBLIC_API_URL=http://localhost:5001/api/v1`
  - `API_URL=http://localhost:5001/api/v1`
  - `OPENAPI_SCHEMA_URL=""`
  - `QADAM_PROJECT_ROOT=""` или checkout `qadam-core`

## 7. Изменение env как change package

Любое изменение env-модели считается change package и должно включать:

1. обновление этого документа;
2. обновление [deployment-runbook.md](./deployment-runbook.md), если меняется production runtime;
3. обновление [current-state.md](../project/current-state.md), если поменялось фактическое состояние сервера;
4. обновление [project/project-change-log.md](../project/project-change-log.md);
5. если изменение затрагивает frontend — обновление [frontend-change-log.md](../frontend/frontend-change-log.md).

## 8. Минимальный контроль доступа

- Production env правится только с root-доступом на сервере.
- Репозитории `qadam-core` и `qadam-web` не считаются местом хранения production secrets.
- Любое новое обязательное secret-поле должно сопровождаться обновлением этого документа и runbook.
