From 66d4d6266b2c78e3b13c664014dbb447617edf4b Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Sat, 26 Sep 2026 15:32:41 +0600 Subject: [PATCH] [2026-09-26] eagle: personal/plans/balda-mm-phase4-agents.md --- personal/plans/balda-mm-phase4-agents.md | 123 ++++++++++++++++++++--- 1 file changed, 107 insertions(+), 16 deletions(-) diff --git a/personal/plans/balda-mm-phase4-agents.md b/personal/plans/balda-mm-phase4-agents.md index b0b6bc23..9a6580e1 100644 --- a/personal/plans/balda-mm-phase4-agents.md +++ b/personal/plans/balda-mm-phase4-agents.md @@ -42,8 +42,49 @@ Mac Контейнер-исполните src -> ~/Developer/proj-x ← СИМЛИНК → ✗ БИТЫЙ ``` -**Правило:** внутрь `~/agents` — только **реальные каталоги** или **hardlink**. -Симлинки допустимы **только если цель лежит внутри того же смонтированного корня**. +**Правило:** внутрь `~/agents` — только **реальные каталоги**. Симлинки не работают +на внешние цели, hardlink'ом каталог подключить нельзя (см. §2) → внешние проекты — +только **bind-mount**. + +--- + +## 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` +по требованию вместо прямого монтирования. --- @@ -90,14 +131,43 @@ Mattermost (Alex) │ идемпотентность + таймаут + лимит 1-2 параллельных задач ▼ Контейнер-исполнитель (Mac, Docker) - │ -v ~/agents:/agents:rw - │ -v ~/Developer/:/agents/ ← для внешних проектов (bind, не линк) + │ -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 на ХОСТЕ │ Node 24 + codex + claude + git - │ CLAUDE_CODE_OAUTH_TOKEN из env + │ CLAUDE_CODE_OAUTH_TOKEN из .env ▼ возврат: diff + вывод тестов + коммит → Балда → MM ``` +### Данные 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 | + +**Почему так:** +- пересборка образа `docker build` **не трогает** bind-mounted каталоги → auth переживает + всё; новый образ поднимается уже залогиненным; +- обновление версии CLI внутри образа не сбрасывает сессию; +- откат образа (`--force-recreate` на старый тег) не требует повторного логина; +- токен не попадает в слои образа → не утечёт при `docker save`/шаринге; +- агент **не видит** свои токены (они вне смонтированного `/agents`). + +⚠️ `~/cli-auth` обязан быть на **том же томе**, что и корень Docker-маппинга, иначе +проект упрётся в virtiofs-ограничения (см. §1a). + --- ## 4. Секция «Agents» в Eagle Dashboard @@ -117,13 +187,14 @@ Mattermost (Alex) ### Что делает новая секция 1. **Список содержимого `~/agents`** — по каждому проекту: имя, тип (реальная папка / - symlink / hardlink), размер, дата изменения, **статус линка** (валиден / битый). + mount / битый symlink), размер, дата изменения, **статус**. 2. **Создать проект** — `mkdir ~/agents/`. -3. **Подключить внешний проект** — **bind-mount-запись**: сохраняется в - `~/Developer/agents-mounts.yaml`, мост перезапускает контейнер с новым `-v`. - ⚠️ **Не symlink** — см. §1. -4. **Hardlink-операции** — `ln -F ~/agents//` (только файлы, - только внутри тома; в UI показывать `st_dev` и блокировать при разных ФС). +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`. @@ -180,12 +251,22 @@ fails closed elsewhere»*. Для Mattermost `ask` **фейлится закры ## 6. План работ (по шагам) -### Шаг 1 — `~/agents` + контейнер-исполнитель (~2 ч) -1. `mkdir ~/agents` + `.gitkeep`, права `admin:staff`. +### Шаг 1 — `~/agents` + `~/cli-auth` + контейнер-исполнитель (~2 ч) +1. `mkdir ~/agents`, права `admin:staff`; `~/cli-auth/{claude,codex}` (0700). 2. `~/Docker/balda-executor/Dockerfile` — Node 24 + `codex` + `claude` + `git`. -3. `docker-compose.yml` — `-v ~/agents:/agents:rw`, `CLAUDE_CODE_OAUTH_TOKEN` из `.env`. -4. Авторизация: `claude setup-token` на Mac → токен в `~/Docker/balda-executor/.env` (0600). -5. Проверка: `docker exec ... claude --version` + `codex --version`. +3. `~/Docker/balda-executor/docker-compose.yml`: + ```yaml + volumes: + - /Users/admin/agents:/agents:rw + - /Users/admin/cli-auth/claude:/root/.claude:rw # auth на хосте + - /Users/admin/cli-auth/codex:/root/.codex:rw # auth на хосте + ``` + + `agents-mounts.yaml` подключается генератором override-файла + (`compose.override.yaml`), чтобы composefile не правился скриптом вручную. +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` остаётся залогинен. ### Шаг 2 — Мост на Mac (~3-4 ч) 1. `~/Developer/agents-bridge/` — FastAPI, порт **8790**. @@ -220,9 +301,12 @@ 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 | --- @@ -232,11 +316,18 @@ fails closed elsewhere»*. Для Mattermost `ask` **фейлится закры 1. Порт моста — **8790** (взят свободный; проверить `lsof -i :8790` перед стартом). 2. Провайдер исполнителя — **claude** (подписка уже залогинена) или **codex**? 3. Нужен ли `ask`-апрув в будущем, или `allow_all` достаточно постоянно? +4. `~/cli-auth` (auth вне `~/agents`, агент не видит) vs `.cli-auth` внутри — по умолчанию **вне**. ## История - **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. ## Связанные заметки - [[personal/projects/balda/balda-mm-delegation-plan]] — общий план (Фазы 4-5)