44 lines
7.5 KiB
Markdown
44 lines
7.5 KiB
Markdown
---
|
||
date: 2026-06-30
|
||
type: decision
|
||
tags: [dttb, mac, ai, agent, open-webui, cptr, netbird, macos-tcc]
|
||
---
|
||
|
||
# cptr на Маке Олега — агент с доступом к РЕАЛЬНЫМ файлам, перенаправление chat.dttb.ru
|
||
|
||
## Контекст
|
||
Песочница cptr в [[2026-06-30-cptr-computer-agent|LXC 146]] делала задачи в изолированном контейнере и **не видела файлы Мака** (разные машины). Олег хотел «полезно по файлам» — агента, который работает с его реальными документами/рабочим столом. Выбор: «безопасно в вакууме» (песочница) vs «полезно, но агент получает доступ к реальной машине». Олег выбрал **второе, осознанно приняв риск** (публичный chat.dttb.ru сможет выполнять команды на Маке).
|
||
|
||
## Решение
|
||
Поставить cptr **на сам Mac** (`MacBookPro.lan`, юзер `ai`, LAN `10.0.0.180`, NetBird `100.70.242.212`, macOS 26.5.1) и **перенаправить** на него коннект cptr в chat.dttb.ru (idx4). Песочницу LXC 146 заглушить (`pct stop 146`).
|
||
|
||
## Что развёрнуто на Маке
|
||
- **Установка:** `uv tool install cptr` → бинарь `/Users/ai/.local/bin/cptr` (Docker на Маке нет, uv 0.11.7 есть). Данные `~/.cptr/app.db`.
|
||
- **Автозапуск:** LaunchAgent `~/Library/LaunchAgents/ru.dttb.cptr.plist` (`cptr run --host 0.0.0.0 --port 8000 --headless`, RunAtLoad+KeepAlive, лог `~/Library/Logs/cptr.log`, env `PYTHONUNBUFFERED=1` чтобы стартовый токен попадал в лог). Загрузка: `launchctl bootstrap gui/$(id -u) …plist`.
|
||
- **Admin** `dttb`/`App5870w`; **/v1-ключ** `sk-cptr-9RGc…WkQ` (им ходит Open WebUI).
|
||
- **Мозг** OmniRoute `cc/claude-opus-4-8`, api_key `lan-trust` — но **по NetBird** (см. грабли), base_url `http://100.70.92.138:20128/v1`.
|
||
- **Workspace** `/Users/ai` → модель `cptr/ai` (стартовый cwd; терминал агента не ограничен этой папкой — у него права юзера `ai` на весь Мак).
|
||
- **chat.dttb.ru:** idx4 перенаправлен с песочницы (10.0.0.166) → `http://10.0.0.180:8000/v1`, ключ Мака, `model_ids:["cptr/ai"]`, name «cptr (Mac)». Open WebUI (LXC 142, **без NetBird**) ходит до Мака по домашней LAN `10.0.0.180`.
|
||
|
||
## ⚠️ Главная грабля: macOS Local Network privacy (TCC) рубит LAN из launchd
|
||
**Симптом:** мозг не подключался — `verify` отдаёт `All connection attempts failed` к `10.0.0.179:20128`, **хотя** интерактивный `curl`/`python` с Мака до этого IP дают 200. Discriminating-тест: cptr до **публичного** `api.openai.com` соединяется (отдаёт «Invalid API key»), до **LAN** — «All connection attempts failed».
|
||
**Причина:** macOS 15+/26 **Local Network privacy** блокирует обращения **фонового launchd-процесса** к локальной сети (10.0.0.0/24 на en0). Промпта на грант для headless-демона нет → тихий отказ. Локальные `run_command` агента (ls, cat и т.п.) — НЕ сетевые, работают; режется только исходящая сеть cptr к LAN.
|
||
**Фикс (без грантов TCC):** ходить к OmniRoute по **NetBird (utun100)** вместо LAN. У хоста OmniRoute (LXC 132) NetBird-IP `100.70.92.138`, OmniRoute слушает 0.0.0.0:20128. Трафик к `100.70.x` идёт через utun (VPN), который **Local Network gate не трогает** → `verify` = `Connected`. **Правило: на маке для launchd-сервиса, которому нужен LAN-ресурс, давай ему адрес ресурса по NetBird, а не 10.0.0.x.**
|
||
|
||
## Безопасность (риск принят Олегом)
|
||
- Агент исполняет тулы под юзером `ai` → доступ ко **всему Маку** этого юзера (файлы, терминал). Это и было целью.
|
||
- Гейт к `/v1`: Bearer-ключ (только у Open WebUI) + LAN-достижимость + admin-only Open WebUI. cptr-UI на 8000 — логин `dttb`/`App5870w`. Application Firewall на Маке **выключен**.
|
||
- cptr слушает `0.0.0.0:8000` → при роуминге на чужом Wi-Fi порт открыт в той сети (гейт — логин/ключ). Для дома ок; если важно — биндить на NetBird-IP или включить firewall.
|
||
- «Рулить откуда угодно» работает, пока **Mac в домашней сети** (Open WebUI без NetBird тянется только по 10.0.0.180). Для полного роуминга — добавить NetBird на LXC 142 (отложено).
|
||
- Мозг агента зависит от NetBird на Маке (100.70.92.138). NetBird ляжет → агент без мозга (`reference_mac_sudo` для рестарта).
|
||
|
||
## ⚠️ Грабля 2: cptr теряет контекст между ходами в Open WebUI
|
||
**Симптом:** в UI Олег ответил «Да» на предложение агента — cptr: «мы ещё ничего не обсуждали». Каждое новое сообщение приходило как пустая сессия.
|
||
**Причина:** cptr — агент с **серверной сессией**, склеивает ходы по заголовку **`X-OpenWebUI-Chat-Id`** (без него — каждый запрос = новая сессия, историю из тела он НЕ использует). Open WebUI по умолчанию этот заголовок **не шлёт** (`ENABLE_FORWARD_USER_INFO_HEADERS` не задан = false). Проверено напрямую: при стабильном `X-OpenWebUI-Chat-Id` cptr помнит (ход2 без истории в теле → верный ответ); без него — забывает.
|
||
**Фикс:** на Open WebUI (LXC 142) выставлен `ENABLE_FORWARD_USER_INFO_HEADERS=true` (форвардит `X-OpenWebUI-User-*` + `X-OpenWebUI-Chat-Id`). Это env, не persistent-config → контейнер пересоздан: `docker inspect open-webui` → дамп всех env в `/root/owui.env`, добавлен флаг, `docker run … --env-file /root/owui.env` (том `open-webui` тот же, логины/токены живы; старый контейнер держал как `open-webui-old` для отката, затем удалил). E2E через Open WebUI с одним `chat_id`: ход1 «ок» → ход2 (без истории) «73» = контекст держится.
|
||
|
||
## Проверено (e2e)
|
||
chat.dttb.ru → `cptr/ai` → агент на Маке → `hostname; sw_vers; ls ~/Desktop|wc -l` = `MacBookPro.lan` / `macOS` / `16`. Список `~/Desktop` совпал с реальным. Прямой вызов cptr `/v1` и через Open WebUI — оба ок.
|
||
|
||
См. [[2026-06-30-cptr-computer-agent]] (песочница-предшественник), [[../projects/dttb/proxmox-inventory]], [[../projects/dttb/credentials]].
|