| IP | 169.58.76.183 — сайты (PTR `fr-ub2gs3.veilix.cc`, прямая запись совпадает); 173.212.216.183 — основной, через него по умолчанию идёт исходящий трафик (PTR `vmi3593751.contaboserver.net`) |
current -> releases/… — симлинк на работающий релиз
shared/.env — секреты prod
shared/media/ — загруженные файлы prod
/home/umami/umami/ — Umami (пользователь umami)
```
vx — личный пользователь администратора, к проекту отношения не имеет. Под vx_prod нет редактора и ИИ-агентов, VSCode Remote под vx_prod не подключаем ([D-004](decisions/D-004-environments.md)). Домашние папки всех пользователей — `750`, владелец — сам пользователь. Пустые папки `/home/vx/veilix.app`, `/home/vx/my.veilix.app`, `/home/vx/tg.veilix.app` проекту не нужны.
## 3. Процессы
1. Каждый сервис — под своим пользователем ОС, как systemd user-юнит. Root один раз включает `loginctl enable-linger` для vx_dev, vx_prod и umami, чтобы юниты работали без входа в систему.
2.`veilix-dev.service` (vx_dev): `pnpm dev` на 127.0.0.1:3100 из `/home/vx_dev/veilix_dev`, перезапуск при сбое. Файл юнита — `ops/systemd/veilix-dev.service` в репозитории, подключён через `systemctl --user link`.
3.`veilix.service` (vx_prod): `pnpm start` на порту 3000 из `/home/vx_prod/veilix/current`, `NODE_ENV=production`, перезапуск всегда.
4.`umami.service` (umami): Umami на порту 3300.
5. Фоновые задачи Payload выполняются внутри процессов приложения ([02-architecture.md](02-architecture.md), раздел 7).
6. Логи — journald, у каждого пользователя свои (`journalctl --user -u veilix` под vx_prod). Сессии Claude логи prod не видят: при разборе проблем prod их присылает пользователь.
7. Проверка живости — `/svc/health` (с проверкой базы).
| plc.veilix.cc | 127.0.0.1:3000 | только разрешённые IP |
| dev.veilix.app, dev.my.veilix.app, dev.veilixvpn.com, dev.my.veilixvpn.com | 127.0.0.1:3100 | только разрешённые IP или пароль; `X-Robots-Tag: noindex` |
| dev.tg.veilix.app, dev.tg.veilixvpn.com | 127.0.0.1:3100 | открыт для Telegram; приложение пускает только разрешённые Telegram-аккаунты |
| dev.plc.veilix.cc | 127.0.0.1:3100 | только разрешённые IP |
| umi.veilix.cc | 127.0.0.1:3300 | только разрешённые IP |
1. Слушает 169.58.76.183, порты 80 (редирект на HTTPS и проверка certbot) и 443. TLS 1.2 и 1.3, HTTP/2, HSTS.
2. Сертификаты — certbot, проверка через HTTP.
3. Сжатие — brotli и gzip.
4. GeoIP: блок `geoip2` в `/etc/nginx/conf/ip.conf`, база `/var/lib/GeoIP/GeoLite2-Country.mmdb`с`auto_reload`. Переменная `$geoip_country_code` уходит приложению заголовком `X-Geo-Country`; такой же заголовок от клиента nginx затирает. Старый модуль GeoIP v1 (`modules-enabled/50-mod-http-geoip.conf`) отключён: он занимает те же имена переменных, и `nginx -t` падает с ошибкой «the duplicate "geoip_country_code" variable».
5. Заголовки к приложению — общий файл `/etc/nginx/snippets/veilix_proxy.conf` (плюс `maps/upgrade.conf` для WebSocket), его подключает каждый `location`с`proxy_pass`: `Host`, `X-Real-IP` и `X-Forwarded-For` (оба — `$remote_addr`: nginx стоит первым, цепочку от клиента не принимаем), `X-Forwarded-Proto`, `X-Geo-Country`, заголовки WebSocket (нужны горячей перезагрузке dev). `proxy_set_header` не наследуется в `location` со своими `proxy_set_header`, поэтому файл подключается в каждый такой `location`.
6.В`nginx.conf` подключаются только `sites-enabled/*.conf`: файлы сайтов должны заканчиваться на `.conf`. Сайты: `sites-available/02_vx_veilix-prod.conf`, `02_vx_veilix-dev.conf`, `02_vx_plc-prod.conf`, `02_vx_plc-dev.conf`. Сертификаты Let's Encrypt (certbot, проверка через nginx): `veilix.app` (6 prod-хостов брендов), `dev.veilix.app` (6 dev-хостов), `plc.veilix.cc`, `dev.plc.veilix.cc`.
a. Доступ по IP — файл `conf/ip_list_admin.conf` со строками `allow …;`, в `location` после него `deny all;`. Подключается на хостах админки и dev, кроме `dev.tg.*`.
b. `dev.tg.*` — отдельный `server` без ограничения по IP, с `X-Robots-Tag: noindex`.
c. На порту 80 — `location / { return 301 https://$host$request_uri; }`. Именно внутри `location`, а не на уровне `server`: так certbot при продлении может вставить свой `location` для проверки домена.
7. Лимиты запросов (`limit_req`) на эндпоинты входа и формы.
8. Для tg.* (этап 3) разрешить показ во фрейме Telegram Web (`frame-ancestors` для доменов telegram.org).
9. Пути трекера Umami на доменах брендов проксируются на 127.0.0.1:3300 ([08-integrations.md](08-integrations.md), раздел 8).
10. Конфигурацию готовит Claude текстом на согласование, применяет root.
## 5. PostgreSQL
1. Базы `veilix_dev`, `veilix_prod` и `umami`. Владелец каждой — роль с именем пользователя ОС: `vx_dev`, `vx_prod`, `umami`.
2. Вход по Unix-сокету с проверкой пользователя ОС: стандартное правило Ubuntu `local all all peer` в `pg_hba.conf` пускает пользователя ОС только под ролью с тем же именем. Паролей нет, `pg_hba.conf` и `pg_ident.conf` править не нужно.
3. У каждой базы снято право подключения для всех (`REVOKE CONNECT … FROM PUBLIC`): войти может только владелец. vx_dev в `veilix_prod` не попадёт.
4. Роль владеет своей базой: Payload нужны права на изменение схемы (push в dev, миграции в prod).
5. Строка подключения: `postgresql://vx_dev@/veilix_dev?host=/var/run/postgresql` (для prod — `vx_prod@/veilix_prod`).
## 6. Почта (Postfix)
1.`smtp_bind_address = 169.58.76.183`, `myhostname = fr-ub2gs3.veilix.cc`: письма уходят с IP, у которого обратная запись совпадает с прямой.
2. OpenDKIM: ключи для veilix.app и veilixvpn.com (селектор, напр. `mail`).
a. SPF: `v=spf1 ip4:169.58.76.183 include:<почтовый хостинг> -all`. Хостинг нужен, потому что ответы поддержки уходят через него (пункт 5); плюс SMTP-провайдер, если используем;
5. Почта поддержки (Q-16): ящики `help@veilix.app` и `help@veilixvpn.com` — на внешнем почтовом хостинге, MX-записи доменов указывают на него (пользователь, R-18). На этапе 2 Chatwoot забирает письма по IMAP и отвечает через SMTP хостинга. Доменов брендов в `mydestination` Postfix нет и быть не должно: тогда письма администраторам на `help@…` уходят на хостинг, а не остаются на этом сервере.
1. Prod-база: каждую ночь `pg_dump -Fc veilix_prod`, хранить 14 ежедневных и 8 еженедельных, копия на другой сервер (куда — Q-14). Раз в месяц — проверочное восстановление в отдельную базу.
2.`shared/media` — синхронизация на тот же внешний сервер.
3. Dev не бэкапим: код — в git, данные dev — тестовые.
| R-00 | 0 | Переезд разработки: перенести `/home/vx/veilix_dev` в `/home/vx_dev/veilix_dev` (владелец vx_dev); SSH-ключ vx_dev для VSCode; Claude Code под vx_dev | сделано |
| R-01 | 0 | Репозиторий в Gitea; SSH-ключ vx_dev с правом push; для vx_prod — ключ только на чтение (deploy key) | vx_dev — сделано: `origin` — `ssh://git@gte.apr.sh:26789/andreypr207/veilix.app.git`, ключ `~/.ssh/veilix_gitea_ed25519` добавлен в Gitea как deploy key с правом записи, `main` отправлен. Ключ vx_prod — на этапе 1h |
| R-02 | 1a | PostgreSQL: роли `vx_dev`, `vx_prod`, `umami` и их базы (раздел 5) | сделано и проверено: vx_dev входит в `veilix_dev`, в `veilix_prod` — отказ |
| R-06 | 1a | MaxMind: `geoipupdate` (ключ в `/etc/GeoIP.conf`, база в `/var/lib/GeoIP`, таймер `geoipupdate.timer`); отключить старый модуль GeoIP v1; блок `geoip2` (раздел 4, пункт 4) | сделано |
| R-07 | 1c | Postfix, OpenDKIM, DNS-записи SPF, DKIM, DMARC — или данные SMTP-провайдера | |
| R-08 | 1h | PostgreSQL для prod — если не сделано вместе с R-02 | сделано в R-02 |
| R-09 | 1h | nginx и сертификаты для prod-хостов | сделано вместе с R-05 |
| R-10 | 1h | Umami: nginx и сертификат для `umi.veilix.cc` (DNS уже есть; база — в R-02) | |
| R-11 | 1h | Бэкапы prod-базы и media на внешний сервер (Q-14) | |
| R-12 | 2 | VPS с Chatwoot и Twenty, ключи API; 2 бота поддержки в BotFather | |
| R-13 | 3 | Боты в BotFather: 2 основных и тестовые; домен для Telegram Login; адреса MiniApp | |
| R-15 | 1h | По желанию (Q-18): sudo-правило, разрешающее vx_dev запускать от имени vx_prod только команду выкладки | |
| R-16 | 1h | По желанию: правило nftables — к 127.0.0.1:3000 подключаются только `www-data` (nginx) и vx_prod ([10-security.md](10-security.md), раздел 7) | |
| R-17 | 1a | nginx dev: отдельный `location /_next/hmr`с`proxy_read_timeout 1h` во всех трёх server-блоках dev на 443: `dev.plc.veilix.cc`, сайты и кабинеты, `dev.tg.*`. В Next 16.4 WebSocket горячей перезагрузки — `/_next/hmr`, и сервер может молчать дольше 30 секунд, а `nginx.conf` закрывает соединение после 30 секунд тишины (`proxy_read_timeout 30`) | сделано: на всех семи dev-хостах соединение держится 70 секунд без сообщений |
| R-18 | 1h | Почта поддержки (Q-16): ящики `help@veilix.app` и `help@veilixvpn.com` на почтовом хостинге, MX-записи доменов; хостинг — в SPF (раздел 6). До этого письма администраторам и заявки видны только в админке и журнале писем | |