Развёртывание Remote Hub
Remote Hub хранит учётные данные провайдеров, каталог и статистику на одном хосте. Авторизованные клиенты обращаются непосредственно к его плоскости данных. Контур управления отделён: необязательный listener привязан только к 127.0.0.1 и обслуживает панель и /api/*, но не /v1/*, /healthz, /readyz или WebSocket. Не публикуйте 10101 и не используйте Tailscale Funnel.
Роли и границы доверия
Заголовок раздела «Роли и границы доверия»standalone объединяет всё на одной машине; hub владеет секретами и статистикой; client хранит только состояние подключения и отдельный ключ данных.
ocx connect https://hub-name.tailnet-name.ts.net --pairing-code-stdinocx connect statusocx syncКлюч клиента записывается в защищённый service-api-token, а не в config.json. При подключении статистика читается с hub и фильтруется по apiKeyId; после отключения используется локальное хранилище. Зеркалирования нет.
Admin token разрешает обычное управление, но никогда не создаёт consent session. Для действий с согласием нужны gui-session, совпадающий Origin и CSRF. Заголовок Tailscale-User-Login доверен только отдельному management ingress; точные логины задаются в remoteGui.allowedTailscaleUsers.
Сервис и Tailscale Serve
Заголовок раздела «Сервис и Tailscale Serve»ocx config set runtimeRole hubocx config set hostname 100.64.0.10ocx config set hub.managementPublicOrigin '"https://hub-name.tailnet-name.ts.net"'ocx config set corsAllowOrigins '["http://localhost:10100"]'ocx config set hub.managementIngress '{"enabled":true,"port":10101}'ocx config set remoteGui.allowedTailscaleUsers '["operator@example.com"]'export OPENCODEX_API_AUTH_TOKEN="$(openssl rand -hex 32)"ocx service installsystemd/launchd читает секрет из service-api-token; plist и unit не содержат его значения.
curl --fail --silent http://100.64.0.10:10100/healthzcurl --fail --silent http://100.64.0.10:10100/readyztailscale serve --bg --https=443 http://127.0.0.1:10101tailscale serve status/healthz подтверждает только работу процесса. Проверьте также /readyz, авторизованный GET /v1/catalog и реальный ответ модели. Собственный TLS-прокси должен использовать tailscale cert hub-name.tailnet-name.ts.net и проксировать только на 127.0.0.1:10101. Не подделывайте Tailscale-User-*; без доверенной идентификации используйте одноразовое pairing.
OAuth, ротация и отключение
Заголовок раздела «OAuth, ротация и отключение»ocx config set oauthOpenBrowser falseocx connect rotate --pairing-code-stdin# только HTTPS:ocx connect rotate --admin-token-stdinOAuth запускается через POST /api/oauth/login. Если callback недоступен, передайте итоговый URL или код как {provider,input} в POST /api/oauth/login/code. Не помещайте код в argv или логи.
При ротации старый и новый ключи действуют под одним apiKeyId не более десяти минут. Старый ключ сохраняется в service-api-token.prev, новый устанавливается атомарно и проверяется через /v1/catalog, затем подтверждается. При неопределённом результате повторите команду с временными полномочиями и не удаляйте кандидаты до проверки.
ocx disconnect восстанавливает локальное состояние даже без hub, но не отзывает удалённый ключ. После отключения отзыв возможен только на странице hub Integrations → API Keys. ocx connect revoke --admin-token-stdin доступен только пока клиент подключён.
Docker и устранение неполадок
Заголовок раздела «Docker и устранение неполадок»Официального Docker-образа нет, но репозиторий содержит поддерживаемые Dockerfile и compose.yaml для локальной сборки Bun-образа, закреплённого по digest. Перед первым запуском один раз передайте ключ данных через stdin; он не выводится и сохраняется с доступом только для владельца в volume ocx-state.
На хосте нужны Git и Bun. Перед каждой сборкой создавайте канонический манифест из отслеживаемых Git исходников и не меняйте их до завершения сборки. Сгенерированный JSON не добавляйте в Git; .git исключён из контекста Docker. По умолчанию порт хоста привязан к 127.0.0.1. Для удалённого доступа явно задайте OPENCODEX_BIND_ADDRESS=<LAN-или-Tailscale-IP> docker compose up -d; 0.0.0.0 открывает все интерфейсы. Защитите доступ брандмауэром и аутентифицированным TLS/tailnet-фронтендом.
Сборка отклоняет устаревший манифест, сверяя каждый SHA-256 с файлами контекста и затем образа. Отсутствующие или изменённые файлы, лишние исходники и символические ссылки запрещены. Обязательны package.json, bun.lock и единственный включаемый файл из scripts/ — scripts/model-metadata.source.json.
git clone https://github.com/lidge-jun/opencodex.gitcd opencodexbun scripts/generate-compatibility-version.tsdocker compose buildopenssl rand -hex 32 | docker compose run --rm -T hub bun run docker/bootstrap-token.tsdocker compose up -dКонтейнер работает от непривилегированного пользователя bun, с корневой файловой системой только для чтения и публикует только 10100. Не публикуйте 10101 и не помещайте секреты в ARG, ENV, COPY, Compose, историю образа или argv. После healthcheck отдельно проверьте readiness, аутентифицированный каталог и реальный запрос. docker compose down сохраняет volume; docker compose down --volumes удаляет также конфигурацию, учётные данные и ключ.
- При недоступном hub можно отключиться офлайн, но отзыв ключа останется незавершённым.
- LKG сохраняется только при временном сбое; при ошибке auth, схемы, размера или протокола локального fallback нет.
- Для
.prevсохраните оба файла и повторите ротацию с временными полномочиями. hub-too-new/hub-too-oldуказывает, какую сторону обновить; локальные записи ещё не сделаны.- Pairing одноразовый, попытки ограничены 429; потерянный код создайте заново.
- Для не-loopback HTTP нужен
--allow-insecure-http; admin token по HTTP не отправляется. - Logout/expiry браузерной сессии не отзывает ключ данных.
- Перед
tailscale serve resetпросмотрите все mappings черезtailscale serve status.

