---
name: cehoproxy_multiuser_and_pool
description: "CehoProxy: одна установка обслуживает всех пользователей терминального сервера; ротация по общему пулу нод всех подписок; пароль и CLI через панель"
metadata: 
  node_type: memory
  type: project
  originSessionId: cf75944b-cc91-4988-aab6-28363391917a
  modified: 2026-08-07T12:04:24.468Z
---

Второй заход по [[cehoproxy_overview]], 07.08.2026. Всё проверено живьём в одноразовой Ubuntu-VM (lima).

**Терминальный сервер: ставить каждому не нужно.** Туннель один на машину, правило движка смотрит на ПУТЬ программы, поэтому действует для всех, кто её запускает. Проверено: `worker2` запустил `/opt/testapp/curl`, `root` — `/opt/secondapp/fetcher`, обе программы одновременно показали «через туннель 1, напрямую 0», лабораторный узел принял трафик обеих.

**Из этого следует пароль.** Панель слушает петлю, а петля доступна КАЖДОМУ, кто вошёл на сервер. Пароль (PBKDF2, 210k итераций) закрывает панель и команды; админа не спрашиваем — он и так может править файл руками.

**Грабля доступа, поймана живьём: `File.Exists` при закрытой папке возвращает false**, и продукт показывал обычному пользователю пустой список вместо отказа — «подписок 0, программ 0» на настроенной машине. Признак установленной системы — публичный указатель `panel.port` рядом с настройками; папка 755, сам конфиг 600.

**CLI обычного пользователя работает через панель.** Нет прав на файл → команда уходит на `/api` службы с паролем, выполняется её же бинарником от root и возвращает тот же текст. Белый список команд; `uninstall`, `daemon`, `setup` удалённо запрещены. Так CLI остаётся полноценным на сервере без графики, где человек не администратор.

**Ротация переделана по образцу xray на mini** (`~/Services/netproxy-xray/xray_subscription_refresh.py`): парсим ВСЕ подписки, складываем в один пул с дедупликацией по (протокол, сервер, порт, логин), а живую выбирает `urltest` движка. Переключать подписку целиком неверно: в одной подписке часть нод живая, часть мёртвая. Фоновая проверка теперь про другое — если не отвечает ВЕСЬ пул, перекачиваем подписки и пересобираем правила.

**Страны — галочками, как в ops_dashboard** (там `allow_countries` → `--allow-country`). Список стран строится ИЗ ПУЛА, а не из результатов замера: замер при поднятом туннеле запрещён, и иначе раздел пустовал бы ровно тогда, когда он нужен. **Настройку, при которой в пуле не остаётся нод, продукт не сохраняет** — объясняет причину и откатывает; иначе человек уходит с конфигурацией, при которой туннель не поднимется.

**Локализация ru/en** — весь текст в `Strings.cs`, язык спрашивается мастером `setup`, меняется командой `lang` и в панели. Обращение на «вы» по правилу [[bots_single_voice_vy]].

**Логотип брали не тот.** Марка КодоЦеха — наковальня, `codoceh.ru/kodoceh-icon.png`; встроена в бинарник как data:URI (панель обязана открываться и без сети). Акцент бренда на сайте — `#2E8C66`.

**Замер задержек в lima-VM недостоверен:** user-mode NAT принимает TCP к ЛЮБОМУ адресу, мёртвая нода на 203.0.113.7 показывалась живой. Тот же класс, что gvisor-TUN. Изоляцию доказывать логом своего узла, а не пингом.

**Терминальные агенты заворачиваются переменными окружения, а не правилом.** У них нет своей программы: их запускает общий интерпретатор, и `process_path_regex` совпадёт с ИНТЕРПРЕТАТОРОМ, а не с агентом. Команда `chp run <команда>` выставляет HTTP(S)_PROXY/ALL_PROXY на локальный вход и запускает. Замерено: `curl` слушается переменных сам, **node до 24 их игнорирует — нужен `NODE_USE_ENV_PROXY=1`**. Доказано логом узла: с `chp run` узел принял 1242 байта, без него ноль. Прав не требует — заворачивать свой процесс может любой пользователь, порт берётся из публичного указателя `panel.port` (там два числа: панель и прокси).

**Скопировать интерпретатор в изолированную папку не выйдет:** хардлинк node создаётся, но не запускается — `dyld: Library not loaded: @rpath/libnode.dylib`, он ищет библиотеки рядом с собой. Поэтому переменные окружения, а не обёртка.

**Короткая команда `chp`** — симлинк (на Windows `chp.cmd`), а не алиас оболочки: алиас живёт только в интерактивной сессии и не виден скриптам, службам и PowerShell. Создаётся мастером `setup`, командой `chp alias` и при обновлении.

**Обновление** `chp update`: релизы GitHub, сравнение версий, ассет `cehoproxy-<os>-<arch>`, замена себя через rename (на Windows запущенный exe нельзя перезаписать, но можно переименовать), перезапуск службы. В панели две кнопки. Репозиторий в конфиге `UpdateRepo`, дефолт `codoceh/cehoproxy`.

**Автоопределение ИИ-инструментов** (`chp detect` и раздел в панели): Claude, Codex, Cursor, Gemini, Grok, Antigravity, ChatGPT по известным путям и PATH. Две грабли, обе поймал живьём: **битая символическая ссылка проходит `File.Exists`** (Cursor «нашёлся» через ссылку на отмонтированный том установщика — теперь проверяется исполняемость цели), и **realpath раскрывает симлинк до пути с НОМЕРОМ ВЕРСИИ** (`~/.grok/bin/grok` → `downloads/grok-0.2.106-...`, после обновления правило молча отвалилось бы — берём папку `~/.grok`).

**Постоянный перевод команды на туннель — `chp wrap`:** обёртка с тем же именем в `~/.local/bin` вызывает оригинал через `chp run`. Проверено логом узла. Ключевая проверка, без которой это была бы мина: **обёртка перехватывает, только если её каталог идёт в PATH РАНЬШЕ оригинала** — если нет, продукт говорит об этом прямо. И `wrap` при выключенной защите ЛОМАЕТ команду (fail-closed) — поэтому в сообщении сразу подсказка `chp unwrap`.

**Полировка после замечаний владельца, каждое было настоящим дефектом:**
- Команды в панели были шаблонами (`chp run <команда>`) — при копировании не работают. Теперь подставляется найденный инструмент.
- У подписок был статус «не проверялась» без кнопки проверки. Кнопка добавлена, показывается и дата.
- Русские комментарии внутри CSS уезжали клиенту и торчали в английской версии. Пояснения перенесены в C#-документацию класса.
- Плейсхолдер поля пути был захардкожен по-русски и оставался русским в EN.
- Имя программы бралось из папки, а у homebrew это НОМЕР ВЕРСИИ: в списке появлялась запись «8.20.0».
- **Команда без прав на запись падала со стеком вызовов**, а в remote-режиме подвисала, спрашивая пароль там, где пароля нет. Теперь: отдельная проверка права ЗАПИСИ (чтения мало), запрос к панели сначала без пароля, и глобальный перехват необработанных исключений.
- Раздел «Терминальные агенты» пропадал, когда ИИ-инструменты не найдены, — а он нужен всегда.

**Логотип в футере** приведён к виду с сайта: контейнер со скруглением, подложкой и тонкой рамкой, картинка заполняет его целиком (`object-fit: cover`), без внутренних отступов. Латиницей бренд пишется `CodoCeh` — как домен.

**Установка своими силами, без сторонних инсталляторов** — `chp install`: копирует программу в системную папку, заводит короткую команду, правит PATH, по согласию СКАЧИВАЕТ движок с сайта автора (это действие пользователя, а не раздача с нашей стороны — GPL не нарушаем) и сразу запускает мастер. Установщик под Windows (`Установить CehoProxy.cmd`, двойной клик и UAC) и скрипт Inno Setup просто вызывают ту же команду — логика одна.

**Три дефекта удаления и установки, найденные полным кругом «поставить → поработать → снести»:**
- **Автозапуск ссылался на путь, ОТКУДА запускали установщик** (`/tmp/cehoproxy`), а не на установленную копию. Файл удаляют — служба ломается. Теперь `OwnExecutablePath` предпочитает установленную копию.
- **`disable` не останавливает службу:** после удаления на машине оставался живой туннель и открытая панель. Добавлен `StopService()` перед снятием с автозапуска.
- **Мягкая остановка не срабатывает, если pid-файл потерян** (службу мог запустить не мы). Добавлено принудительное завершение СВОИХ процессов строго по пути нашего конфига — чужой sing-box не трогаем.
- Мелочи: `panel.port` не удалялся, скачанный нами движок оставался, мастер повторно создавал ярлык рядом с временной копией.

**Проверено полным кругом на чистой VM:** установка → служба systemd `active`, выход DE → удаление, после которого нет ни бинарника, ни папки, ни службы, ни процессов, ни занятых портов, ни правил маршрутизации, а сеть машины работает.

**Имена файлов в релизе жёстко связаны с обновлением:** `chp update` ищет ассет `cehoproxy-<os>-<arch>`; назовёте иначе — обновление молча не найдёт файл.

**Windows перепроверен живьём 07.08.2026** после всего рефакторинга: полное удаление старой установки, чистая установка новой (движок скачался сам), автозапуск через планировщик, панель, прокси для браузера (выход DE через SOCKS5) и изоляция Codex — 9 процессов, 2 соединения через туннель, 0 напрямую.

**Прогон вскрыл три дефекта проверки изоляции, невидимых на Linux и macOS:**
- **`p.Id` читался ПОСЛЕ `p.Dispose()`** — вся проверка падала с «No process is associated with this object». Классическая ловушка `foreach` с `finally { Dispose() }` и обращением к объекту после блока.
- **В подсчёт попадали слушающие сокеты и закрывающиеся хвосты** (`FIN_WAIT_2`, `TIME_WAIT`): они идут с прежнего адреса, и сразу после включения защиты продукт показывал «ТЕЧЁТ» при исправной изоляции. Считать только `ESTABLISHED`; названия состояний TCP Windows не переводит, опираться на них можно.
- **Соединения внутри самой машины** через туннель идти не могут и утечкой не являются.

**`tasklist` по имени видит один процесс Codex, а правило — девять:** вспомогательные процессы называются иначе (`ChatGPT`), и это ровно тот случай, ради которого изоляция идёт по ПАПКЕ, а не по файлу.

**Состояние подписки должно ставиться по факту загрузки нод из неё**, а не по общей пробе выхода: мёртвая подписка, вернувшая пустой ответ, показывалась как «работает».

**Подписка `sub.vlessi.cloud` на 07.08.2026 отдаёт пустой ответ** (не 502, а именно пустоту) — у владельца нет рабочей подписки, туннель без неё не поднимется.

Связано: [[cehoproxy_crossplatform_plan]], [[cehoproxy_github_publish]], [[process_path_must_be_physical]].
