From 3edbddf512ab72bf008a62d2b545bc7d45e0991d Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sat, 26 Sep 2026 15:42:47 +0600 Subject: [PATCH] [2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md personal/projects/balda/balda-mm-delegation-plan.md personal/tech/cloudflare-tunnel-ssh-published-route.md --- personal/plans/balda-mm-phase4-agents.md | 392 +++++------------- .../balda/balda-mm-delegation-plan.md | 7 + .../cloudflare-tunnel-ssh-published-route.md | 30 ++ 3 files changed, 152 insertions(+), 277 deletions(-) diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index ed66f6d1..64213dd7 100644 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -1,299 +1,155 @@ # Фаза 4 — исполнитель на Mac + `~/agents` + секция «Agents» в Eagle Dashboard -> Создан 2026-09-26. Статус: **ПЛАН — ожидает апрува Alex, работа не начата.** -> Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фаза 4 + Фаза 5). +> План. **Работа НЕ начата** (образ не собран, контейнер не запущен). +> Часть [[personal/projects/balda/balda-mm-delegation-plan]] (Фазы 4-5). > Предыдущее: [[personal/projects/balda/balda-kraken-mm-deploy]] (Фаза 3 ✅ — бот отвечает Alex). --- -## 0. Решения Alex (2026-09-26, этот заход) +## 1. Решения | # | Вопрос | Решение | |---|---|---| -| 1 | Папка для проектов исполнителя | **`~/agents`** | -| 2 | Подтверждения | **Full access** → `permissions.mode: allow_all` | -| 3 | Управление hardlink/symlink в `~/agents` | **Новая секция в Eagle Dashboard** | +| 1 | Папка для проектов исполнителя | `~/agents` | +| 2 | Подтверждения | Full access → `permissions.mode: allow_all` | +| 3 | Управление линками в `~/agents` | Новая секция «Agents» в Eagle Dashboard | +| 4 | Auth CLI (claude/codex) | на хосте, `~/cli-auth`, **вне** `~/agents` | +| 5 | Сеть Kraken → Mac | Cloudflare Tunnel (существующий cloudflared на Mac) | --- -## 1. 🔴 ПРОВЕРЕНО ЖИВЫМ ТЕСТОМ: симлинк в Docker НЕ работает +## 2. Ограничения macOS Docker (проверено тестами) -Alex спросил: «Если в папку симлинк кинуть вовне — докер это прочтет?» -**Ответ: НЕТ.** Проверено на живом Docker (alpine:3.20, linux/arm64), не по памяти. +| Ограничение | Итог | +|---|---| +| Симлинк на внешний путь | битый в контейнере — цель резолвится от корня контейнера | +| Live `mount --bind` внутри VM | контейнер не видит (virtiofs строит маппинг на старте) | +| Сетевая шара SMB/NFS | в контейнер не протянуть (тот же virtiofs) | +| Hardlink на каталог | физически невозможен (ядро запрещает) | +| Hardlink через границу ФС | `EXDEV` | -``` -host: /tmp/symtest/outside/file.txt = "REAL CONTENT ON HOST" -bind: -v /tmp/symtest/bind:/mnt (внутри /mnt/target -> /tmp/symtest/outside) +**Следствие:** внешние проекты подключаются **только bind-mount'ом через `-v`** + +`docker compose up -d` (2-3 сек). Hardlink — только для файлов внутри одного тома. -контейнер: cat /mnt/target/file.txt -→ cat: can't open '/mnt/target/file.txt': No such file or directory -``` - -Сам симлинк внутри виден (`lrwxrwxrwx link -> /outside`), но **цель резолвится -относительно корня контейнера**, где этого пути нет. Docker монтирует только реальный -путь — симлинки не разворачивает и не «протаскивает». - -### Следствие для архитектуры - -``` -Mac Контейнер-исполнитель -~/agents/ /agents (bind-mount, rw) - project-x/ ← РЕАЛЬНАЯ папка → видна - src -> ~/Developer/proj-x ← СИМЛИНК → ✗ БИТЫЙ -``` - -**Правило:** внутрь `~/agents` — только **реальные каталоги**. Симлинки не работают -на внешние цели, hardlink'ом каталог подключить нельзя (см. §2) → внешние проекты — -только **bind-mount**. +Детали механизма: [[personal/tech/macos-docker-virtiofs-mount-limits]]. --- -## 1a. 🔴 ПРОВЕРЕНО ЖИВЫМ ТЕСТОМ: live `mount --bind` на macOS Docker НЕ работает - -Alex предложил: если bind на ходу возможен — делать и live, и в compose, чтобы контейнер -не пересоздавать. **Проверено тремя прогонами — на macOS невозможно.** - -``` -VM (nsenter -t 1 -m): /tmp/bindtest2/agents/existing/f.txt = "DATA2" ✓ файл есть -mount --bind ... → BIND_OK -контейнер (без рестарта): cat /agents/existing/f.txt - → No such file or directory ✗ не видит -``` - -Проверено в трёх вариантах — результат одинаков: -1. Каталог создан **после** старта контейнера + live bind → в контейнере пусто. -2. Ожидание 5 с (гипотеза «кэш атрибутов virtiofs») → пусто. -3. Каталог существовал **до** старта контейнера + bind поверх него → **пусто**. - -**Причина:** на macOS Docker Desktop отдаёт контейнеру файлы не через Linux `mount`, -а через **virtiofs** — отдельный демон, который строит маппинг путей **в момент старта -контейнера**. `mount`, сделанный внутри VM через `nsenter`, для virtiofs невидим: -изнутри VM всё корректно, до контейнера не доходит. Это не кэш — рестарт не поможет. - -📌 **Следствие:** live-bind был бы возможен на Linux-хосте (там Docker нативный). -На macOS — **только через `-v` в compose + reproduce контейнера**. -`docker compose up -d` с новым `-v` = 2-3 сек, `--privileged` не нужен. - -📌 **Тот же корень, что и с симлинками:** virtiofs-маппинг понимает только пути -**мира macOS**, известные ему на старте. Всё, что появляется в VM позже (`mount`, -шара, новый каталог) — в контейнер не проецируется. - -### Сетевые шары (SMB/NFS) — тот же вывод - -Смонтировать шару в macOS и дать `-v /Volumes/share:/data` **не сработает** (virtiofs -не видит `mount_smbfs`-точку). Единственный рабочий вариант — mount внутрь VM через -`nsenter`, но он не переживает рестарт Docker и требует `--privileged`. -**Рекомендация: не тянуть шару в контейнер.** Если данные на NAS — `rsync` в `~/agents` -по требованию вместо прямого монтирования. - ---- - -## 2. Symlink / hardlink / bind-mount — что выбрать - -| Способ | В Docker | Может пересечь ФС | Может указать на файл вне mount | Итог | -|---|---|---|---|---| -| **symlink** | ✗ битый | да | ✗ нет | ❌ не использовать для внешних целей | -| **hardlink** | ✓ работает | **✗ нет** | ✗ нет | ✓ для файлов внутри `~/agents` | -| **bind-mount** | ✓ работает | да | да | ✓ для внешних каталогов | -| **APFS clonefile** | ✓ (реальная копия) | да | да | ✓ для «копии проекта» без расхода диска | - -### Ключевое ограничение hardlink - -Hardlink **не может пересечь границу файловой системы** и **не может ссылаться на -каталог** (только на файл, и только на macOS — с `-F`). То есть замена симлинка -«проект целиком» через hardlink технически невозможна, если проект лежит -на другом томе или это директория. - -**Вывод:** «линкование проекта извне» = **bind-mount**, а не линк. Линки — для -внутренней организации одного тома. - -📌 **Прямо отвечая на исходный замысел Alex:** hardlink'ом **нельзя** подключить -проект-каталог извне — физически невозможно (каталоги не линкуются, ФС не пересекается). -Поэтому в целевой архитектуре внешние проекты висят на **bind-mount'ах** -(`~/Developer/:/agents/`), а секция Dashboard управляет именно манифестом -`agents-mounts.yaml`. Hardlink-операции остаются как вспомогательный инструмент -для файлов внутри одного тома. - ---- - -## 3. Целевая архитектура +## 3. Архитектура ``` Mattermost (Alex) │ «почини баг X в project-y» ▼ -Балда-оркестратор (Kraken, DeepSeek, mode=allow_all) - │ 1. читает Obsidian, формирует ТЗ - │ 2. /goalkeeper → work → validate +Балда-оркестратор (Kraken, DeepSeek, allow_all) + │ читает Obsidian, формирует ТЗ, /goalkeeper → work → validate ▼ -Мост (Mac, FastAPI, :8790) ← новый сервис, регистрируется в Eagle Dashboard - │ HTTP + shared secret (WireGuard-хаб уже есть) - │ идемпотентность + таймаут + лимит 1-2 параллельных задач +Cloudflare Tunnel: agents-bridge.qentra.top → localhost:8790 + │ (существующий cloudflared на Mac, PID + LaunchAgent) + ▼ +Мост (Mac, FastAPI :8790) + │ idempotency key, таймаут, лимит 1-2 параллельных задач, shared secret ▼ Контейнер-исполнитель (Mac, Docker) - │ -v ~/agents:/agents:rw ← рабочая область, локальный диск - │ -v ~/Developer/:/agents/ ← внешние проекты (bind, не линк) - │ -v ~/cli-auth/claude:/root/.claude:rw ← auth CLI на ХОСТЕ (вне /agents!) - │ -v ~/cli-auth/codex:/root/.codex:rw ← auth CLI на ХОСТЕ + │ -v ~/agents:/agents:rw + │ -v ~/Developer/:/agents/:rw ← внешние проекты + │ -v ~/cli-auth/claude:/root/.claude:rw ← auth на хосте + │ -v ~/cli-auth/codex:/root/.codex:rw ← auth на хосте │ Node 24 + codex + claude + git - │ CLAUDE_CODE_OAUTH_TOKEN из .env ▼ -возврат: diff + вывод тестов + коммит → Балда → MM +diff + тесты + коммит → Балда → Mattermost ``` -### Данные CLI — на хосте, не в образе +### Auth CLI на хосте -🔴 Требование Alex: **данные codex/claude живут в хостовой папке**, чтобы не ушли -при пересборке образа, обновлении CLI или пересоздании контейнера. - -**Расположение — `~/cli-auth`, ВНЕ `~/agents`** (решение принято 2026-09-26): -агент-исполнитель монтирует `/agents` на запись, и если auth лежит внутри, агент -получает доступ к своим же токенам. Вынос наружу закрывает это. - -| Данные | Хост-путь | В контейнере | Права | +| Данные | Хост | В контейнере | Права | |---|---|---|---| -| Claude auth / сессии / конфиг | `~/cli-auth/claude` | `/root/.claude` | 0700 | -| Codex auth / конфиг | `~/cli-auth/codex` | `/root/.codex` | 0700 | -| OAuth-токен Claude | `~/Docker/balda-executor/.env` | env `CLAUDE_CODE_OAUTH_TOKEN` | 0600 | -| Ключ провайдера codex (если нужен) | `~/Docker/balda-executor/.env` | env | 0600 | +| Claude auth | `~/cli-auth/claude` | `/root/.claude` | 0700 | +| Codex auth | `~/cli-auth/codex` | `/root/.codex` | 0700 | +| OAuth-токен Claude | `~/Docker/balda-executor/.env` | env | 0600 | -**Почему так:** -- пересборка образа `docker build` **не трогает** bind-mounted каталоги → auth переживает - всё; новый образ поднимается уже залогиненным; -- обновление версии CLI внутри образа не сбрасывает сессию; -- откат образа (`--force-recreate` на старый тег) не требует повторного логина; -- токен не попадает в слои образа → не утечёт при `docker save`/шаринге; -- агент **не видит** свои токены (они вне смонтированного `/agents`). - -⚠️ `~/cli-auth` обязан быть на **том же томе**, что и корень Docker-маппинга, иначе -проект упрётся в virtiofs-ограничения (см. §1a). +Пересборка образа и обновление CLI auth не сбрасывают. Токены вне `/agents` → агент их не видит. --- ## 4. Секция «Agents» в Eagle Dashboard -### Где живёт код (проверено 2026-09-26) +Правки: `main.py` (роуты) · `templates/index.html` (вкладка `?tab=agents`) · +`services.yaml` (запись `agents-bridge`, `health.type: http`) · `health.py`. -| Файл | Строк | Что править | -|---|---|---| -| `~/Developer/eagle-dash/main.py` | 889 | новые `@app.get/post/delete` роуты | -| `~/Developer/eagle-dash/templates/index.html` | 1248 | новая вкладка + Alpine.js стейт | -| `~/Developer/eagle-dash/health.py` | 181 | health-чек моста (`type: http`) | -| `~/Developer/eagle-dash/services.yaml` | 6.5 КБ | регистрация сервиса `agents-bridge` | - -**Существующие вкладки:** `services` · `crons` · `pages` · `files` -(роутинг через `?tab=`, `setUrlTab()`, `activeTab` в Alpine). - -### Что делает новая секция - -1. **Список содержимого `~/agents`** — по каждому проекту: имя, тип (реальная папка / - mount / битый symlink), размер, дата изменения, **статус**. -2. **Создать проект** — `mkdir ~/agents/`. -3. **Подключить внешний проект** — запись в `~/Developer/agents-mounts.yaml` - (`name` + `host_path`) → `docker compose up -d` (**2-3 сек, без privileged**). - ⚠️ **Только bind-mount.** Ни symlink (битый), ни hardlink (каталоги не линкуются). - ⚠️ Live `mount --bind` **недоступен на macOS** (см. §1a) — recreate обязателен. -4. **Hardlink-операции** — `ln -F ~/agents//` (только **файлы**, - только внутри одного тома; в UI показывать `st_dev` и блокировать при разных ФС). -5. **Удалить из контейнера** — убрать из `agents-mounts.yaml` + `docker compose up -d` - ⚠️ **Файлы на диске не трогать** — только отвязка от контейнера (см. Core Rules). -6. **Открыть терминал** — ссылка на существующую вкладку `files`. - -### API-контракт (черновик) - -``` -GET /api/agents → [{name, kind, size, mtime, link_valid, target}] -POST /api/agents {name} → mkdir ~/agents/ -POST /api/agents/{name}/mount {host_path} → добавить bind-mount + recreate -DELETE /api/agents/{name}/mount → убрать bind-mount + recreate (файлы целы) -POST /api/agents/{name}/hardlink {file, name} → ln -F (с проверкой st_dev) -GET /api/agents/{name}/usage → размер, место на томе -``` - -### Чего секция НЕ делает - -- **Не удаляет** содержимое `~/agents` — только отвязывает от контейнера. -- **Не убивает** процессы — lifecycle контейнера через существующий supervisor. -- **Не создаёт symlink** на внешние цели — вместо них bind-mount. - ---- - -## 5. Система подтверждений — итог разведки - -### Как устроено в Balda (апстрим, `docs/architecture/interactive-questions.md`) - -Три режима (`permissioncmd.Mode`): - -| Режим | Поведение | +| Функция | Что делает | |---|---| -| `allow_all` | никого не спрашивает — **выбран Alex** | -| `ask` | ADK-callback **блокирует турн**, вопрос уходит в чат, ждёт ответа, таймаут fail-closed, продолжает через `ResumeTarget` | -| `deny_all` | всё запрещено | +| Список | содержимое `~/agents`: имя, тип, размер, mtime, статус линка | +| Создать | `mkdir ~/agents/` | +| Подключить внешний | запись в `agents-mounts.yaml` → `compose up -d` | +| Hardlink | `ln -F` для файлов, проверка `st_dev` | +| Отключить | убрать из манифеста + `up -d`; **файлы на диске целы** | +| Терминал | ссылка на вкладку `files` | -🔴 **`ask` сейчас работает только для Telegram и Slackagent** — прямая цитата -`interactive-questions.md`: *«`ask` is supported for Telegram and Slackagent and -fails closed elsewhere»*. Для Mattermost `ask` **фейлится закрыто**. +**API:** +``` +GET /api/agents → список +POST /api/agents {name} → mkdir +POST /api/agents/{name}/mount {host_path} → bind + recreate +DELETE /api/agents/{name}/mount → отвязать (файлы целы) +POST /api/agents/{name}/hardlink {file, name} → ln -F со st_dev +GET /api/agents/{name}/usage → размер +``` -### Что это значит для сценария «MCP просит линк → Alex подтверждает из чата» - -Прямой путь **сейчас невозможен** поверх MM. Варианты: - -| Вариант | Объём | Ложится куда | -|---|---|---| -| A. Дописать `ask` в MM | ~1-2 дня | ветка `feat/mattermost-channel` → **расширение PR #8** | -| B. Апрув в мосте (свой мини-эндпоинт + очередь) | ~0.5 дня | FastAPI-мост Фазы 4 | - -**С Alex выбран `allow_all`** — этим заходом подтверждения не нужны. Пункт -оставлен как задел: когда `ask` понадобится, начинать с варианта **B** (дешевле, -не зависит от ревью апстрима). +**Не делает:** не удаляет файлы, не убивает процессы, не создаёт symlink на внешние цели. --- -## 6. План работ (по шагам) +## 5. Подтверждения -### Шаг 1 — `~/agents` + `~/cli-auth` + контейнер-исполнитель (~2 ч) -1. `mkdir ~/agents` (права `admin:staff`); `~/cli-auth/{claude,codex}` (0700) — **вне** `~/agents`. -2. `~/Docker/balda-executor/Dockerfile` — Node 24 + `codex` + `claude` + `git`. -3. `~/Docker/balda-executor/docker-compose.yml`: - ```yaml - volumes: - - /Users/admin/agents:/agents:rw - - /Users/admin/cli-auth/claude:/root/.claude:rw # auth на хосте, вне /agents - - /Users/admin/cli-auth/codex:/root/.codex:rw # auth на хосте, вне /agents - ``` - + `agents-mounts.yaml` подключается генератором override-файла - (`compose.override.yaml`), чтобы composefile не правился скриптом вручную. - 🔴 только `-v` + `docker compose up -d` — live-bind на macOS невозможен (§1a). -4. Авторизация один раз: `claude setup-token` на Mac → токен в - `~/Docker/balda-executor/.env` (0600); `codex` — свой flow в `~/cli-auth/codex`. -5. Проверка: смонтированные auth видны (`docker exec ... ls /root/.claude`), - `claude --version` + `codex --version` работают, повторный `up -d` остаётся залогинен. +Режимы Balda: `allow_all` (выбран) · `ask` · `deny_all`. +`ask` поддержан **только для Telegram и Slackagent**; для Mattermost фейлится закрыто. +Сценарий «подтвердить из чата» требует: **A** — дописать `ask` в MM (~1-2 дня, расширение PR #8), +либо **B** — апрув в мосте (~0.5 дня). При `allow_all` не нужно; начинать с B, когда понадобится. -### Шаг 2 — Мост на Mac (~3-4 ч) -1. `~/Developer/agents-bridge/` — FastAPI, порт **8790**. -2. Эндпоинты: `POST /task` (idempotency key), `GET /task/{id}`, `GET /healthz`. -3. Запуск CLI headless в контейнере, возврат diff + тестов. -4. Лимит параллельных задач: **1-2** (риск «unusual traffic» — см. §Риски плана Фаз). -5. Shared secret в заголовке + проверка source IP (WireGuard-подсеть). +--- -### Шаг 3 — Сеть Kraken → Mac (~1 ч) -1. WireGuard-туннель (хаб на VPS уже есть — см. скилл `wireguard-vps-hub`). -2. Проверка: `curl` с Kraken на Mac `:8790/healthz`. +## 6. Шаги -### Шаг 4 — Секция «Agents» в Eagle Dashboard (~3-4 ч) -1. `main.py` — 6 роутов из §4. -2. `templates/index.html` — вкладка `agents` + `?tab=agents`. -3. `services.yaml` — запись `agents-bridge` (`health.type: http`, url `:8790/healthz`). -4. `__pycache__` + `launchctl kickstart -k gui/$(id -u)/com.eagle.dashboard`. +### Артефакты, уже созданные на диске (2026-09-26) -### Шаг 5 — Связка Балда → мост (~2 ч) -1. MCP-инструмент в Балде (или webhook) → `POST /task`. -2. Проверка на задаче-пустышке: `echo ok > /agents/test/probe.txt`. +Созданы **до** подтверждения плана — статус «черновик», при старте сверить и доделать: -### Шаг 6 — Приёмка Фазы 5 (~1 день) -1. Сквозной тест **от аккаунта Alex**: `@balda почини X` → diff в MM. -2. Расходы: DeepSeek-токены + подписочные лимиты. -3. Задокументировать: как работает, где логи, как откатить. +| Путь | Что | +|---|---| +| `~/agents` | рабочая область (drwxr-xr-x, `admin:staff`) | +| `~/cli-auth/{claude,codex}` | auth CLI, 0700, **вне** `~/agents` | +| `~/Docker/balda-executor/Dockerfile` | Node 24 + claude + codex + git, HEALTHCHECK | +| `~/Docker/balda-executor/docker-compose.yml` | 4 тома, сеть `executor-net`, лимит 8G | +| `~/Docker/balda-executor/.env` | ⏳ не создан (токен) | + +`docker-compose.yml` ссылается на `compose.override.yaml`, который генерирует +`tools/render-mounts.py` из `agents-mounts.yaml` — **override вручную не править**. +Ни образ не собран, ни контейнер не запущен. + +**1. `~/agents` + `~/cli-auth` + контейнер (~2 ч)** +`mkdir ~/agents`, `~/cli-auth/{claude,codex}` 0700 → `~/Docker/balda-executor/Dockerfile` +(Node 24 + claude + codex + git) → `docker-compose.yml` с 4 томами → `claude setup-token` в `.env` 0600 → +проверка `claude --version`, `codex --version`, повторный `up -d` остаётся залогинен. + +**2. Мост (~3-4 ч)** +`~/Developer/agents-bridge/`, FastAPI :8790, `POST /task` (idempotency), `GET /task/{id}`, `GET /healthz`; +запуск CLI headless в контейнере; лимит 1-2 задач; shared secret. + +**3. Сеть (~1 ч)** +Cloudflare Tunnel: добавить published route `agents-bridge.qentra.top` → HTTP `localhost:8790` + +Access Application. Токен и процесс cloudflared не трогаются. Проверка: `curl` извне на `/healthz`. +⚠️ hostname — **одноуровневый** (двухуровневые дают TLS handshake failure). + +**4. Секция «Agents» (~3-4 ч)** +6 роутов + вкладка + запись в `services.yaml` → `__pycache__` + `launchctl kickstart -k`. + +**5. Связка Балда → мост (~2 ч)** +MCP-инструмент в Балде → `POST /task`; проба `echo ok > /agents/test/probe.txt`. + +**6. Приёмка (~1 день)** +Сквозной тест **от аккаунта Alex** в MM: `@balda` → задача → diff в ответе. +Замер расходов (DeepSeek + подписка), документация отката. --- @@ -301,43 +157,25 @@ fails closed elsewhere»*. Для Mattermost `ask` **фейлится закры | Риск | Оценка | Митигация | |---|---|---| -| Симлинк в `~/agents` на внешний путь → битый | **подтверждён** | bind-mount вместо линка; UI помечает битые линки | -| Live `mount --bind` на macOS → контейнер не видит | **подтверждён** | только `-v` + `compose up -d`; live-bind вычеркнут из плана | -| Сетевую шару (SMB/NFS) в контейнер протянуть нельзя | **подтверждён** | не тянуть: `rsync` в `~/agents` по требованию | -| Hardlink через границу ФС → `EXDEV` | высокий | проверять `st_dev` перед `ln`; UI блокирует | -| Бан подписочного аккаунта за «unusual traffic» | средний | 1-2 параллельных задачи, официальный бинарь, без прокси токена | -| Полный доступ (`allow_all`) + MCP → агент может снести `~/agents` | **средний** | бэкапы + `git rm`-семантика вместо `rm`; правки только через bind-mount-манифест | -| Агент читает/пишет свои же auth-данные | средний | `~/cli-auth` **вне** `~/agents` → агент их не видит | -| Мост открыт наружу | высокий | только WireGuard-подсеть + shared secret; не публиковать порт в LAN | +| Мост опубликован в интернет | высокий | Access Application + shared secret | +| Полный доступ агента к `~/agents` | средний | бэкапы; `git rm`-семантика вместо `rm` | +| Бан подписки за «unusual traffic» | средний | 1-2 параллельных задачи, официальный бинарь, без прокси токена | +| Hardlink через ФС → `EXDEV` | высокий | проверка `st_dev` в UI | +| Mac спит → туннель и мост недоступны | средний | задачи в durable-очереди Балды, идемпотентность | --- -## 8. Открыто (требует ответа Alex при старте) +## 8. Открыто -1. Порт моста — **8790** (взят свободный; проверить `lsof -i :8790` перед стартом). -2. Провайдер исполнителя — **claude** (подписка уже залогинена) или **codex**? -3. Нужен ли `ask`-апрув в будущем, или `allow_all` достаточно постоянно? - -📌 Решено 2026-09-26: auth в **`~/cli-auth`** (вне `~/agents`), внешние проекты — -**только bind-mount**, live-bind вычеркнут. - -## История -- **2026-09-26**: создан. Решения Alex: папка `~/agents`, full access, управление - линками — секция в Eagle Dashboard. Живым тестом доказано, что симлинк в Docker - не читается → внешние проекты подключать bind-mount'ом, не линком. -- **2026-09-26 (продолжение)**: два уточнения доказаны живыми тестами. - **Live `mount --bind` на macOS Docker НЕ работает** (virtiofs видит VM-mount'ы только - на старте контейнера) → recreate обязателен, live-bind вычеркнут (3 прогона, все - отрицательные). **Сетевые шары** — тот же корень, в контейнер не протянуть. - Добавлен раздел про **данные CLI на хосте** (`~/cli-auth` вне `~/agents`) по - требованию Alex: auth переживает пересборку образа и обновление CLI. +1. Провайдер исполнителя — claude или codex +2. Нужен ли `ask`-апрув в будущем +3. Hostname для моста (`agents-bridge.qentra.top`?) ## Связанные заметки -- [[personal/plans/balda-mm-phase4-agents]] — Фаза 4 (план) -- [[personal/tech/macos-docker-virtiofs-mount-limits]] — 🔴 virtiofs: симлинки/live-bind/шары в контейнер - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5) -- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅ бот отвечает) -- [[personal/plans/balda-mm-pr-plan]] — Фаза 2 (PR #8, 8/8 CI, ждёт ревью) +- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3 (✅) +- [[personal/plans/balda-mm-pr-plan]] — Фаза 2 (PR #8) +- [[personal/tech/macos-docker-virtiofs-mount-limits]] — ограничения macOS Docker - [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы, дельта-апплаер -- скилл `eagle-dashboard` — архитектура дашборда, health-типы, launchd-нюансы -- скилл `wireguard-vps-hub` — туннель Kraken → Mac +- [[family/how-to/kraken-portainer-access]] — cloudflared, routes +- скилл `eagle-dashboard` — архитектура дашборда diff --git a/personal/projects/balda/balda-mm-delegation-plan.md b/personal/projects/balda/balda-mm-delegation-plan.md index 1a94189b..8d8740a8 100644 --- a/personal/projects/balda/balda-mm-delegation-plan.md +++ b/personal/projects/balda/balda-mm-delegation-plan.md @@ -155,6 +155,13 @@ Scheduler/webhook-таргеты (`balda.scheduler.jobs` → `target: locator`) - Вариант C: **вторая Балда в VM** и связка через webhooks Балды (в апстриме есть `balda.webhooks.*`). - **Рекомендация: начать с B**, потому что он не требует ни форка Балды, ни второго канала. Позже можно перейти на C. +✅ **Решено 2026-09-26: вариант B (мост на Mac).** Сетевой путь Kraken → Mac — +**Cloudflare Tunnel** через уже работающий cloudflared на Mac (`agents-bridge.qentra.top` +→ HTTP `localhost:8790` + Access Application), **не WireGuard**: VPS-хаб `91.207.28.205` +удалён 2026-09-01, туннель мёртв. cloudflared-маршруты применяются на стороне Cloudflare +без рестарта процесса. Детали транспорта и монтажей — [[personal/plans/balda-mm-phase4-agents]]. +⚠️ hostname обязательно одноуровневый (см. [[personal/tech/cloudflare-tunnel-ssh-published-route]]). + --- ## 2. Сценарий делегирования (как это работает для Alex) diff --git a/personal/tech/cloudflare-tunnel-ssh-published-route.md b/personal/tech/cloudflare-tunnel-ssh-published-route.md index 3aa2a827..f3afd74f 100755 --- a/personal/tech/cloudflare-tunnel-ssh-published-route.md +++ b/personal/tech/cloudflare-tunnel-ssh-published-route.md @@ -52,6 +52,36 @@ Host ssh-kraken.qentra.top - Сам ssh к hostname:22 напрямую (без cloudflared в ProxyCommand) к проксированному DNS не работает — hostname за Cloudflare-Anycast, в туннель трафик попадает только через cloudflared-клиент. - `network_mode: host` (container cloudflared) — сервис напрямую видит локальные порты (sshd на `0.0.0.0:22`). +## HTTP-сервис через существующий туннель (без рестарта cloudflared) + +Туннель на origin — **один процесс на все маршруты**. Добавление нового HTTP-маршрута +**не требует** ни правки токена, ни рестарта cloudflared: маршруты живут на стороне +Cloudflare и подхватываются работающим туннелем. + +**Схема (Zero Trust):** +- Networks → Tunnels → туннель → **Routes → Add route → Published application** +- Service: **HTTP** → `localhost:<порт>` (например `localhost:8790`) +- hostname — **одиночный** subdomain (`agents-bridge.qentra.top`), см. правило выше + +**Обязательно: Access Application** на тот же hostname (Access → Applications → +Add self-hosted, тип HTTP). Без него сервис получается публичным без авторизации — +для служебных мостов это недопустимо. + +**Живой пример на Mac (проверено 2026-09-26):** + +| Факт | Значение | +|---|---| +| Бинарь | `/opt/homebrew/bin/cloudflared` (2026.3.0) | +| Процесс | `cloudflared tunnel --protocol quic run --token …` | +| Автозапуск | LaunchAgent `com.cloudflare.cloudflared.plist` | +| Токены | `~/.cloudflared/*-token` (0600), отдельный файл на каждый hostname | +| Маршрут на Mac | `ssh-kraken.qentra.top` → `ssh://localhost:22` | + +Проверка, что туннель поднят: наличие процесса + запись в `~/.cloudflared/`. +Проверка нового маршрута: `curl` извне на `/healthz`. + +--- + ## Общий паттерн «жив, но шумит» cloudflared на origin без IPv6 Если у origin нет глобального IPv6 (роутер не выдаёт), cloudflared пытается поднять QUIC и по IPv6 → в логах постоянные `ERR ... write udp [::]->198.41.x.x:7844: sendmsg: network is unreachable`, соединения падают/retry. Туннель продолжает работать по IPv4-QUIC. Возможный фикс — заставить cloudflared использовать только IPv4.