# NODE.DC Synology deploy Эта папка фиксирует текущий воспроизводимый NAS-deploy для `nodedc-platform` на Synology RS1221RP+. ## Правила - Не выполнять `docker stop`, `docker restart`, `docker compose down`, `docker system prune` для старых проектов. - Новый compose project: `nodedc-platform`. - Новая папка на NAS: `/volume1/docker/nodedc-platform`. - Внутренний HTTP edge использует `18080`, AI Workspace Hub — `18081`, Tasker upstream — `18090`, BIM Viewer upstream — `18100`, Ops Agents Gateway upstream — `18190`. - Старые порты `9000` и `5678` заняты старым `nodedc-demo` и не используются. ## Deploy runner `nodedc-deploy` is managed outside the platform runtime tree. The synced install candidate lives in: ```text /volume1/docker/nodedc-deploy/runner-install/nodedc-deploy ``` The live root-owned runner is: ```text /usr/local/sbin/nodedc-deploy ``` `deploy-current.sh` syncs the runner candidate into `runner-install`. Promoting it to live is explicit: ```bash sudo install -o root -g root -m 0755 \ /volume1/docker/nodedc-deploy/runner-install/nodedc-deploy \ /usr/local/sbin/nodedc-deploy sudo /usr/local/sbin/nodedc-deploy verify-install ``` ## Текущие внешние домены ```text https://id.nodedc.ru -> Authentik https://hub.nodedc.ru -> Launcher / Hub https://ops.nodedc.ru -> Tasker / Operational Core https://bim.nodedc.tech -> BIM Viewer https://ops-agents.nodedc.ru -> Ops Agents Gateway / MCP https://ai-hub.nodedc.ru -> AI Workspace Hub / WebSocket relay ``` `id.nodedc.ru` is the user-facing OIDC/login host. Authentik Admin is intentionally not exposed through this public host; `/if/admin/*` returns `404` there. `ai-hub.nodedc.ru` is intentionally routed by Synology DSM Reverse Proxy directly to `172.22.0.222:18081`, because this endpoint must carry WebSocket Upgrade traffic for remote Codex agents. В `Caddyfile.http` эти домены проксируются через локальный HTTP edge, но upstream получает `X-Forwarded-Proto: https` и `X-Forwarded-Port: 443`. `bim.nodedc.tech` can either be routed by DSM directly to the BIM host port `172.22.0.222:18100`, or through the platform HTTP edge `172.22.0.222:18080`. When it goes through the platform edge, `BIM_DOMAIN=bim.nodedc.tech` and `SYNOLOGY_BIM_VIEWER_UPSTREAM=host.docker.internal:18100` keep the same target service. ## AI Workspace Hub / Assistant contract `ai-workspace-hub` remains a thin public WebSocket relay. It is the only AI Workspace service exposed through DSM/Nginx as `https://ai-hub.nodedc.ru`. `ai-workspace-assistant` is an internal platform service for per-user executors, selected device, shared threads and installer generation. It must not be exposed as a public route. Launcher, Engine and Tasker use it through Docker networking: ```env NODEDC_AI_WORKSPACE_ASSISTANT_URL=http://ai-workspace-assistant:18082 PLANE_NODEDC_AI_WORKSPACE_ASSISTANT_URL=http://ai-workspace-assistant:18082 ``` Installer generation must always point remote Codex workers to the deployed Hub: ```env AI_WORKSPACE_HUB_PUBLIC_URL=wss://ai-hub.nodedc.ru/api/ai-workspace/hub AI_WORKSPACE_HUB_INTERNAL_URL=https://ai-hub.nodedc.ru AI_WORKSPACE_HUB_FALLBACK_URLS= ``` Server-side Hub API calls, including executor status checks, use `AI_WORKSPACE_HUB_INTERNAL_URL` and require a token accepted by Hub: `AI_WORKSPACE_HUB_TOKEN`, `NDC_AI_WORKSPACE_HUB_TOKEN`, or the shared `NODEDC_INTERNAL_ACCESS_TOKEN` where that token is intentionally common across platform services. Local NDC SEO Mode development uses this same server-side Hub class through `SEO_AI_WORKSPACE_PROFILE=deployed-hub`, `SEO_AI_WORKSPACE_CONTROL_URL=https://ai-hub.nodedc.ru`, and a Hub-accepted `SEO_AI_WORKSPACE_CONTROL_TOKEN`. The Hub only exposes an Assistant-compatible allowlist proxy for setup/probe/thread dispatch calls behind bearer-token auth; it does not expose unrestricted Assistant, product apps, Authentik, Engine, Ops, or Tasker to the remote Codex worker. AI Workspace Assistant also calls product entitlement adapters before every Codex run. Ops uses the Agent Gateway internal endpoint; the token must match `NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN` from the Ops Agent Gateway deployment: ```env AI_WORKSPACE_OPS_ENTITLEMENT_URL=http://172.22.0.222:18190/api/internal/v1/ai-workspace/entitlements AI_WORKSPACE_OPS_ENTITLEMENT_TOKEN= AI_WORKSPACE_OPS_ENTITLEMENT_REQUIRED=false ``` ## Локальные домены для первичной проверки На Mac для первичной проверки добавить в `/etc/hosts`: ```text 172.22.0.222 auth.nas.nodedc 172.22.0.222 auth-admin.nas.nodedc 172.22.0.222 launcher.nas.nodedc 172.22.0.222 task.nas.nodedc ``` Первичные URL: ```text http://auth.nas.nodedc:18080 http://auth-admin.nas.nodedc:18080/if/admin/ http://172.22.0.222:18080/if/admin/ http://launcher.nas.nodedc:18080 http://task.nas.nodedc:18080 http://task.nas.nodedc:18090 ``` `auth-admin.nas.nodedc` is the technical Authentik Admin entrypoint. `172.22.0.222:18080` is a local IP fallback for workstations without `auth-admin.nas.nodedc` DNS. Both keep Authentik access separate from the public `id.nodedc.ru` login entrypoint and do not load NODE.DC auth-flow CSS. ## Что входит - `docker-compose.platform-http.yml` поднимает новый Authentik, Launcher, Notification Core, AI Workspace Hub и Caddy edge. - `Caddyfile.http` маршрутизирует локальные `auth/auth-admin/launcher/task.nas.nodedc`, IP fallback `172.22.0.222` для Authentik Admin и внешние `id/hub/ops.nodedc.ru`. - `deploy-current.sh` синхронизирует compose, Caddyfile, Notification Core source, AI Workspace Hub source и опционально Launcher/BIM source в NAS mount. Authentik templates синхронизируются только при явном `SYNC_AUTHENTIK_TEMPLATES=1`. - `backup-current.sh` делает snapshot Launcher runtime/uploads/Auth templates/config, BIM Viewer `server/data` и готовит команду `pg_dump` для Authentik Postgres. - Tasker поднимается отдельным compose из `NODEDC_TASKMANAGER/plane-app/docker-compose.yaml` на порту `18090`. - Ops Agents Gateway поднимается отдельным compose из `NODEDC_TASKMANAGER_CODEXAPI/docker-compose.synology.yml` на `172.22.0.222:18190`; Synology reverse proxy должен вести `ops-agents.nodedc.ru` на этот порт, а не на `18090`. ## Внутренний Authentik API для Launcher Launcher подключен и к platform identity-сети, и к engine-сети. В engine-сети тоже может быть сервис с DNS-именем `authentik-server`, поэтому это имя нельзя использовать для `NODEDC_AUTHENTIK_BASE_URL`: Docker DNS может отдать не тот Authentik, и platform API-token будет получать `403 Token invalid/expired`. Для platform Authentik зафиксирован отдельный alias: ```text http://nodedc-platform-authentik-server:9000 ``` Live `/volume1/docker/nodedc-platform/platform/.env.synology` должен содержать: ```env NODEDC_AUTHENTIK_BASE_URL=http://nodedc-platform-authentik-server:9000 AUTHENTIK_BASE_URL=http://nodedc-platform-authentik-server:9000 NODEDC_AUTHENTIK_SERVICE_TOKEN= AUTHENTIK_SERVICE_TOKEN= ``` Быстрая проверка из launcher-контейнера: ```bash sudo /usr/local/bin/docker exec nodedc-platform-launcher-1 sh -lc ' echo "$NODEDC_AUTHENTIK_BASE_URL" getent hosts nodedc-platform-authentik-server for attempt in 1 2 3 4 5 6 7 8 9 10; do wget -qSO- \ --header "Authorization: Bearer $NODEDC_AUTHENTIK_SERVICE_TOKEN" \ "$NODEDC_AUTHENTIK_BASE_URL/api/v3/core/groups/?search=nodedc_admin" \ 2>&1 | head -n 25 && exit 0 echo "authentik-api-not-ready attempt=$attempt" sleep 10 done exit 1 ' ``` Ожидаемый результат: `HTTP/1.1 200 OK`. После изменения platform Authentik alias пересоздавать нужно `authentik-server`, `authentik-worker` и `launcher`. Один `authentik-server` может временно отдавать `503 Service Unavailable`, пока worker и bootstrap не готовы. ## Синхронизация текущего состояния С Mac, при смонтированном `/Volumes/docker`: ```bash cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform NAS_ROOT=/Volumes/docker/nodedc-platform \ LAUNCHER_REPO=/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher \ BIM_REPO=/Users/dcconstructions/Downloads/mnt/NODEDC/NODEDC_BIM_VIEWER \ TASKER_REPO=/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER \ TASKER_CHANGED_BASE=533f8c6 \ GATEWAY_REPO=/Users/dcconstructions/Downloads/mnt/data/NODEDC_TASKMANAGER_CODEXAPI \ ./infra/synology/deploy-current.sh ``` Скрипт не запускает Docker сам: на NAS `sudo` интерактивный, поэтому команды применения печатаются в конце. По умолчанию sync source-копий на SMB/NAS не сохраняет owner/group/perms/times. Для Docker build важен контент, а macOS/Synology metadata может падать на `utimensat Operation timed out`. Если metadata действительно нужно сохранить для отдельного ручного случая, запускать с `RSYNC_PRESERVE_METADATA=1`. Что синхронизируется: - Platform compose/Caddy. - Notification Core source в `/volume1/docker/nodedc-platform/platform/notification-core`. - AI Workspace Hub source в `/volume1/docker/nodedc-platform/platform/ai-workspace-hub`. - Authentik templates только при `SYNC_AUTHENTIK_TEMPLATES=1`; по умолчанию они не трогаются, чтобы лёгкий Hub deploy не уносил экспериментальную тему/брендинг в prod. - Launcher source в `/volume1/docker/nodedc-platform/launcher/source`. - BIM Viewer source в `/volume1/docker/nodedc-platform/bim-viewer/source`; `server/data` не синхронизируется и остаётся live runtime-хранилищем моделей, shares, comments и refs. - Tasker `plane-app/docker-compose.yaml` и, если задан `TASKER_CHANGED_BASE`, только изменённые source-файлы из диапазона `TASKER_CHANGED_BASE..HEAD`. - Ops Agents Gateway source в `/volume1/docker/nodedc-platform/ops-agents`. Полный sync Tasker source по SMB тяжёлый для Plane fork. Использовать его только осознанно: ```bash TASKER_SYNC_SOURCE=1 ./infra/synology/deploy-current.sh ``` Секретные runtime env-файлы не перетираются: - `/volume1/docker/nodedc-platform/platform/.env.synology` - `/volume1/docker/nodedc-platform/bim-viewer/source/.env` - `/volume1/docker/nodedc-platform/tasker/plane-app/.env.synology` - `/volume1/docker/nodedc-platform/ops-agents/.env` Если emergency-fix был сделан прямо на Synology в этих env-файлах, перенести sanitized-значение в `.env.synology.example`/docs, а секрет оставить только в live env. ### Gelios: сбор всех units доверенного подключения `GELIOS_UNIT_SCOPE=all` означает **все текущие и будущие units только одной уже настроенной пары `GELIOS_TENANT_ID` + `GELIOS_CONNECTION_ID`**. Это не wildcard на другие tenants или providers. Provider credential остаётся только в Engine. Пропавший из очередного ответа unit не удаляется: его stable provider ID и `last_seen_at` сохраняются; видимость на карте — отдельная логика витрины. Перед каноническим `nodedc-deploy apply` включить политику на Synology (скрипт создаёт backup и не выводит секреты): ```bash sudo bash /volume1/docker/nodedc-deploy/inbox/prepare-gelios-all-units-env.sh ``` Затем применить узкий Platform-артефакт, собранный с `--gateway-only`, через `nodedc-deploy`. В его plan должны быть только `gelios-postgres` и `gelios-gateway`; общий `docker-compose` в такой архив не входит. После apply Gateway начинает принимать весь состав units этого подключения. ## AI Hub relay-only deploy Для локального тестирования с внешней Codex-машиной нельзя деплоить Launcher, Engine, Ops, Authentik или Tasker. Единственная допустимая удалённая точка в этой схеме — `ai-workspace-hub`, потому что удалённый worker должен иметь публичный WebSocket/HTTP relay. С Mac, при смонтированном `/Volumes/docker`, синхронизировать только Hub source, compose и узкий apply-script: ```bash cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform NAS_ROOT=/Volumes/docker/nodedc-platform ./infra/synology/sync-ai-hub-relay.sh ``` На Synology применить только `ai-workspace-hub`: ```bash ssh dctouch@100.109.216.21 'cd /volume1/docker/nodedc-platform/platform && sudo bash apply-ai-hub-relay.sh' ``` Этот путь намеренно делает только: - build `nodedc/ai-workspace-hub:local`; - `docker compose up -d --no-deps --force-recreate ai-workspace-hub`; - container health check; - проверку `/api/ai-workspace/hub/v1/assistant-relays//poll`; - public route check через `https://ai-hub.nodedc.ru`. Он не меняет `.env.synology`, не синхронизирует Launcher/Tasker/Ops source, не пересоздаёт Authentik, reverse-proxy, базы, storage или product containers. После успешного relay-only deploy локальная цепочка для теста должна быть такой: ```text local Launcher/Engine/Ops/Auth/Assistant -> deployed AI Hub relay: https://ai-hub.nodedc.ru -> remote Codex worker -> deployed AI Hub relay -> local Assistant relay poll -> local product services ``` ## Лёгкое обновление Hub / Launcher Для правок только в Launcher/BFF не пересоздавать Authentik, reverse-proxy, Tasker и Ops Agents: ```bash cd /volume1/docker/nodedc-platform/launcher/source sudo /usr/local/bin/docker build --no-cache -t nodedc/launcher:local . cd /volume1/docker/nodedc-platform/platform sudo /usr/local/bin/docker compose \ --env-file /volume1/docker/nodedc-platform/platform/.env.synology \ -f /volume1/docker/nodedc-platform/platform/docker-compose.platform-http.yml \ up -d --force-recreate --no-deps ai-workspace-hub launcher ``` После такого deploy проверить `healthz`, запись в launcher storage/uploads и сценарий пользователя без аппрува: сохранение аватара не должно показывать экран `Заявка ожидает подтверждения`. Дополнительно проверить, что live bundle больше не содержит старый pending gate: ```bash launcher_asset="$( curl -k -sS --compressed -H 'Accept: text/html' https://hub.nodedc.ru/ \ | grep -aoE 'index-[A-Za-z0-9_-]+\.js' \ | head -n 1 )" test -n "$launcher_asset" if curl -k -sS --compressed "https://hub.nodedc.ru/assets/${launcher_asset}" \ | grep -aq 'Заявка ожидает подтверждения'; then echo 'old pending gate still present' exit 1 fi echo 'launcher-pending-gate-ok' ``` ## BIM Viewer deploy `bim.nodedc.tech` на Synology можно вести напрямую через DSM Reverse Proxy на `172.22.0.222:18100`. В этом режиме Platform Caddy не участвует в публичном BIM-трафике, но `Caddyfile.http` всё равно содержит BIM route как запасной вариант для маршрута через `172.22.0.222:18080`. Canonical BIM deploy now uses `nodedc-deploy` with `component=bim-viewer`; do not deploy BIM by manually running `docker compose up` from the source directory as the normal path. Live BIM root: ```text /volume1/docker/nodedc-platform/bim-viewer/source ``` Live env stays outside artifacts: ```text /volume1/docker/nodedc-platform/bim-viewer/source/.env ``` Before the first canonical apply, prepare `.env` from `.env.synology.example` and verify that `NODEDC_INTERNAL_ACCESS_TOKEN` matches `platform/.env.synology`. After that, `.env` must not be included in deploy artifacts. Canonical deploy commands: ```bash sudo /usr/local/sbin/nodedc-deploy verify-install sudo /usr/local/sbin/nodedc-deploy plan /volume1/docker/nodedc-deploy/inbox/.tgz sudo /usr/local/sbin/nodedc-deploy apply /volume1/docker/nodedc-deploy/inbox/.tgz ``` The `bim-viewer` runner component: - applies payload under `/volume1/docker/nodedc-platform/bim-viewer/source`; - rejects `.env`, `server/data`, upload/model/comment/share runtime storage, `node_modules`, `.git`, shell hooks and backup files; - prepares required live runtime directories under `server/data` before compose; - builds `nodedc/bim-converter:local` from `converter/Dockerfile`; - recreates only `ndc-beam-viewer` and `nodedc-bim-converter`; - healthchecks `http://127.0.0.1:18100/api/auth/session`. Минимальная проверка после apply: ```bash curl -k -fsS http://127.0.0.1:18100/api/auth/session curl -k -sS -o /dev/null -w '%{http_code}\n' https://bim.nodedc.tech/ ``` Ожидаемо: `/api/auth/session` возвращает `authRequired: true`, а прямой public root без BIM-сессии отдаёт `302` в Launcher login/launch. Public share `/share/` должен открываться без авторизации и без toolbar. ## Tasker / OPS BIM iframe rebuild OPS web bundle получает BIM URL на build-time через `VITE_BEAM_VIEWER_BASE_URL` и `VITE_BEAM_API_BASE_URL`. После смены домена пересобрать только web image: ```bash cd /volume1/docker/nodedc-platform/tasker/plane-src VITE_BEAM_VIEWER_BASE_URL=https://bim.nodedc.tech \ VITE_BEAM_API_BASE_URL=https://bim.nodedc.tech \ BUILD_BACKEND=0 BUILD_WEB=1 BUILD_ADMIN=0 \ sh rebuild-nas-legacy.sh ``` Это пересоздаёт только `web` и не трогает PostgreSQL/MinIO volumes. ## Authentik Admin и Brand CSS NODE.DC auth-flow CSS must stay template-scoped. Do not store it in Authentik `Brand.branding_custom_css`: Authentik passes that CSS into Admin/User web component runtime and breaks native controls. After syncing Authentik templates to NAS, recreate `reverse-proxy authentik-server authentik-worker launcher`, then clear existing global Brand CSS in the live DB: ```bash cd /volume1/docker/nodedc-platform/platform sudo bash clear-authentik-brand-css.sh ``` Verify: ```bash id_admin_status="$( curl -k -sS -o /dev/null -w '%{http_code}' https://id.nodedc.ru/if/admin/ )" if [[ "$id_admin_status" != "404" ]]; then echo "public id admin is not closed: status=${id_admin_status}" exit 1 fi echo 'public-id-admin-closed-ok' auth_admin_page="$( curl -k -fsS --compressed http://auth-admin.nas.nodedc:18080/if/admin/ )" printf '%s' "$auth_admin_page" \ | grep -aE '|authentikBrand.branding_custom_css = ""' auth_admin_flow="$( curl -k -fsS --compressed http://auth-admin.nas.nodedc:18080/if/flow/default-authentication-flow/ )" if printf '%s' "$auth_admin_flow" | grep -aq '