# ADR-021 (PR #51): Expose opencode port 4096 on 0.0.0.0 for cross-host NPM proxy ## Статус Accepted (2026-07-24) ## Контекст opencode-config мигрирует на linux-1 (`193.3.168.35`). Web UI opencode слушает порт 4096 (`opencode serve --hostname 0.0.0.0 --port 4096`). Публичный доступ к домену `opencode.slaid098.dev` идёт через NPM (Nginx Proxy Manager, контейнер `jc21/nginx-proxy-manager`), который живёт на linux-2 (`92.119.114.77`) и проксирует `opencode.slaid098.dev` → `193.3.168.35:4096`. Текущий `docker-compose.yml` биндит порт как `127.0.0.1:4096:4096` (localhost-only). Это работало, пока opencode жил на linux-2: Cloudflare named tunnel (`cloudflared`) запускался на том же хосте и обращался к `127.0.0.1:4096` локально. После миграции на linux-1 NPM на linux-2 должен стучаться на публичный IP linux-1 — localhost-only биндинг отвергает соединение на loopback-интерфейсе, NPM не достучится. Топология подтверждена SSH-аудитом (memory `technical/opencode-config-migration-state-2026-07-24.md`): NPM на linux-2, opencode мигрирует на linux-1 (раньше Cloudflare tunnel, теперь NPM-прокси). ## Решение Перевязать порт 4096 в `docker-compose.yml` с `127.0.0.1:4096:4096` на `0.0.0.0:4096:4096`: ```yaml ports: - "0.0.0.0:4096:4096" ``` `0.0.0.0:4096` биндит контейнерный порт 4096 на все интерфейсы хоста, NPM на linux-2 достучится через публичный IP linux-1 (`193.3.168.35:4096`). Дополнительно — 5 тестов в `tests/test_docker_compose.py` (parsed + raw check на оба условия), чтобы предотвратить регрессию обратно к localhost-only. ### Альтернативы - **Оставить `127.0.0.1:4096` + Cloudflare named tunnel на linux-1** — отклонено: миграционный план переводит публичный доступ на NPM-прокси (centralized SSL-termination + domain management на linux-2), Cloudflare tunnel на linux-1 потребовал бы отдельной настройки и токена. NPM уже управляет 9 доменами `*.slaid098.dev`, добавить `opencode.slaid098.dev` проще, чем поднимать второй tunnel. - **Биндить на конкретный публичный IP linux-1 (`193.3.168.35:4096:4096`)** — отклонено: hardcoded IP в compose хрупок (при смене хоста/IP потребует правки файла). `0.0.0.0` переносим между хостами; доступ ограничивается firewall на linux-1 (ufw/iptables, разрешить 4096 только с linux-2 IP), что конфигурируется вне compose и не требует правок при миграции. - **Не открывать, проксировать через SSH-туннель linux-2→linux-1** — отклонено: SSH-туннель — ручная/хрупкая инфраструктура (restart, no systemd unit, no health-check), NPM → прямой TCP — стандартный паттерн reverse proxy. Дополнительный слой без выгоды. - **Поднять NPM на linux-1 (чтобы NPM и opencode на одном хосте, localhost-only OK)** — отклонено: linux-1 не имеет NPM, установка дублирует инфраструктуру (9 доменов уже на linux-2 NPM, SSL-сертификаты, database.sqlite). Миграция opencode не должна тянуть за собой миграцию NPM. ## Альтернативы См. блок «Альтернативы» выше (включён в Решение для единого контекста). Кратко: отклонены — Cloudflare tunnel на linux-1, hardcoded public IP, SSH-туннель, NPM на linux-1. Выбран `0.0.0.0` + firewall на linux-1 (переносимость + security на уровне хоста, не compose).