--- date: 2026-06-08 type: decision tags: [dttb, swarmclaw, openclaw, orchestrator, lxc, docker] --- # SwarmClaw — оркестратор-надстройка над openclaw (LXC 135) ## Контекст Олег искал «аналог openclaw — оркестратор + удобный + самообучение». По ходу выяснилось: 1. У openclaw **самообучение уже работает** (Dreaming, embeddings ready, recall с concept-tagging + spaced repetition — см. правку [[openclaw]]). Менять движок не нужно. 2. **SwarmClaw** ([github.com/swarmclawai/swarmclaw](https://github.com/swarmclawai/swarmclaw)) — это не замена, а **control-plane НАД openclaw**: рой агентов, делегирование, расписания, дашборд, и подключение существующих openclaw-gateway. Ровно «оркестратор» из запроса, без миграции стека. Решение: поставить SwarmClaw на Proxmox (не Mac-app — тот умирает с ноутом) как постоянный дирижёр над Антошкой. ## Где живёт | Параметр | Значение | |----------|----------| | Proxmox LXC | **135** (hostname `swarmclaw`) | | IP | 10.0.0.135/24, gw 10.0.0.1 | | ОС | Debian 12, unprivileged + `nesting=1,keyctl=1` | | Ресурсы | 2 vCPU / 4 GB RAM / 16 GB (rootfs на `work`) | | Рантайм | Docker 29.5 + compose, образ `ghcr.io/swarmclawai/swarmclaw:latest` | | Каталог | `/opt/swarmclaw` (репо), данные volume `./data:/app/data` | | Доступ | `pct exec 135 -- bash`; dashboard `http://10.0.0.135:3456` | ## Доступ к UI/API - **URL:** `http://10.0.0.135:3456` (LAN/NetBird), слушает `0.0.0.0:3456-3457`. - **ACCESS_KEY:** `4613e7d05f51229a96d7b1a0dd34b24e` — он же cookie `sc_auth` для API. - Auth: `POST /api/auth {"key":"..."}` → cookie, либо header `Cookie: sc_auth=`. `getAccessKey()=process.env.ACCESS_KEY`, `validateAccessKey` сравнивает строкой. ## Что настроено 1. **Провайдер OmniRoute** (`custom`, OpenAI-compatible): - `baseUrl=http://10.0.0.179:20128/v1`, `requiresApiKey:false` (внутренний, отдаёт каталог без ключа). - Каталог 58 моделей; инференс `cc/claude-opus-4-8` подтверждён (stream chat.completions). 2. **Агент «Dirizhyor»** (id `0d388a87`) — provider `omniroute`, model `cc/claude-opus-4-8`. 3. **openclaw-gateway как control-plane:** - credential `cred_62caf5d0f0de` (provider `openclaw`, зашифрованный gateway-token). - gateway profile `gateway-0eaf65b0` «Antoshka (openclaw 137)», endpoint `http://10.0.0.239:18789`, `isDefault:true`. - openclaw gateway token: `20bfbdfed25b87b211a6faa60db3ab8fac75fb5100958ac8` (`gateway.auth.mode=token` на LXC 137). - device спарен и **approved** на openclaw (`openclaw devices` → 10.0.0.135, `operator.admin`). - connect `ok:true`. ## Грабли (важно для будущего) 1. **ACCESS_KEY жил только в `/app/.env.local` внутри контейнера** (не в volume). Пересоздание/обновление образа → новый ключ → запертый вход. **Фикс:** скопировал `ACCESS_KEY` (+`CREDENTIAL_SECRET` если будет) в **хостовый** `/opt/swarmclaw/.env.local` (он `env_file` в compose) → ключ постоянен через пересоздания. `CREDENTIAL_SECRET` дополнительно лежит в volume `data/credential-secret`. 2. **Auth rate-limit:** 5 неудачных попыток с IP → лок 15 мин (`authRateLimitMap`, in-memory). Перебор форматов auth (Bearer/x-api-key — оба неверные) триггерит. **Правильный auth — только cookie `sc_auth`.** Сброс лока — `docker compose up -d --force-recreate` (in-memory обнуляется, ключ из env_file сохраняется). 3. **Подключение openclaw требует approve device-pairing.** SwarmClaw `manualConnect` хардкодит `useDeviceAuth=true` (протокол v4, `auth:{token}` + device-signature). openclaw в token-mode создаёт **pending pairing** → пока не сделать `openclaw devices approve ` на LXC 137, connect молча `{"ok":false}`. Token-only handshake (proto4, `auth.token`, scopes `operator.admin`) проверен рабочим. После approve — connect `ok:true`, device в Paired. 4. **`/api/openclaw/doctor` → `spawn openclaw ENOENT`** — в контейнере нет bundled `openclaw` CLI; связь идёт по WS, не через CLI. Безвредно. 5. **DNS-FakeIP не словили** (в отличие от LXC 137): github/docker.com резолвятся честно через `1.1.1.1/8.8.8.8` даже без NetBird на 135. NetBird не ставил. 6. Локаль в LXC не настроена (`locale: Cannot set LC_*`) — косметика, при желании `locale-gen en_US.UTF-8`. 7. **Агент отвечал `Error: Missing credentials … OPENAI_API_KEY`** хотя OmniRoute ключ не требует. Причина: OpenAI-совместимый клиент внутри SwarmClaw отказывается слать запрос без непустого `apiKey`. **Фикс:** создать dummy-credential (`POST /api/credentials {provider:omniroute, apiKey:"omniroute-noauth-dummy"}` → `cred_0d2feda42f7b`) и привязать к провайдеру (`requiresApiKey:true` + `credentialId`) И к агенту (`PUT /api/agents/` — PATCH даёт 405). OmniRoute ключ игнорирует. После — агент Dirizhyor отвечает на Opus 4.8 (проверено: «работает», + сам диагностировал второй агент через tools). 8. **UI «No agents yet» + циклический логин в Chrome** = браузер держал старый JS-бандл (в логах `Failed to find Server Action`), ломались Server Actions (вход и загрузка). Сервер исправен (REST API через NPM HTTPS отдаёт auth+agents). Лечится чистым браузером (Yandex/инкогнито сработали сразу) или Clear site data в Chrome. SW/PWA нет, контейнер стабилен (0 рестартов). 9. **Дефолтный агент Assistant/«Ассистент»** упорно падал `Claude CLI not found`. Корень в коде: `agent-runtime-config.ts` → `provider = seed.provider || 'claude-cli'` (хардкод-фоллбек). У default слетал/пустел provider → фоллбек на claude-cli (в контейнере нет). `POST /api/agents` не даёт задать фиксированный `id`, `PUT /api/agents/default` для пустого не помог. **Фикс: прямой `INSERT OR REPLACE INTO agents` в `data/swarmclaw.db`** — id=`default`, data = копия Dirizhyor (omniroute/opus/cred). App подхватывает без рестарта (loadAgents читает свежо). Теперь оба агента на OmniRoute/Opus. 10. **«Session not found» при тесте через curl** — нельзя слать в произвольный `agent-chat-*` ID; сессия создаётся UI/отдельно. Не баг агента — артефакт диагностики. Реальные сессии (созданные в UI) работают (`completed`). 11. **Gateway health «не обновлялась 30 мин / статус неизвестно»** — у RPC `/api/openclaw/gateway` есть только connect/disconnect/reload-mode; health-поле профиля (`status`,`lastCheckedAt`) обновляет фоновый probe, не RPC. `gateway.connect{profileId}` → `ok:true` (связь рабочая), но индикатор остаётся `unknown`. Косметика мониторинга, на работу не влияет. ## База знаний (vault) подключена к агентам — 2026-06-08 Олег: «подключить базу данных так же, как у code-server и openclaw». SwarmClaw НЕ имеет folder-RAG как openclaw (`memorySearch.extraPaths`) — его memory это agent-memory (dream/graph). Поэтому путь = **как у code-server**: агент через файловые tools работает в каталоге vault. - **Vault склонирован в volume:** `git clone https://oleg:***@git.dttb.ru/oleg/knowledge-base` → `/opt/swarmclaw/data/knowledge-base` (1286 md, 19M). Именно в `data/` (volume), иначе контейнер `/app/data` его не видит. - **Агенты настроены на vault:** `PUT /api/agents/` → `workspace=/app/data/knowledge-base` (Dirizhyor + Ассистент). `workspaceAccess` через PUT не сохранился, но не нужен — агент читает через `shell`/`files` tools. - **Проверено:** на вопрос про openclaw агент сам сделал `grep -ril openclaw /app/data/knowledge-base/projects/dttb` → прочитал `openclaw.md` → ответил точно (LXC 137, 10.0.0.239, 100.70.167.54). RAG-доступ работает. - **Автообновление:** `/root/kb-pull-swarm.sh` (cron `*/15` на LXC 135) — `git pull` vault из Gitea. Как kb-pull у openclaw 137. ### Встроенный Knowledge-раздел (доп. путь, по запросу Олега) SwarmClaw имеет раздел **Knowledge** (`/api/knowledge`), и агент использует его **автоматически**: `prompt-builder` + `selectKnowledgeCitations` инжектят релевантные куски в контекст с цитатами (отдельного tool нет; `knowledge_search`/`knowledge_store` как tools удалены). Поиск — текстовое сходство (jaccard, не embeddings) — для фактов норм. - **Импортировано 189 entries** (`projects`, `decisions`, `snippets`, `claude-memory`, `templates` — без `daily`/`notes` шума): скрипт POST `/api/knowledge {title,content,sourcePath}` по каждому .md. 0 ошибок. - **Ре-синк:** `/root/kb-knowledge-sync.sh` (cron `30 3 * * *`) — git pull + удалить наши entries (по `sourcePath` префиксу) + импорт заново. Иначе POST плодит дубли (нет upsert). - **Итог — два механизма:** (1) Knowledge entries → авто-контекст с цитатами (UI-раздел); (2) файловый workspace → агент grep'ает всю базу (свежее, `*/15`). Дополняют друг друга. ## Схема API (для будущих правок headless) - `POST /api/providers` → `{id,name,baseUrl,models[],requiresApiKey,isEnabled}` (type всегда `custom`), хранит JSON в таблице `provider_configs`. - `POST /api/agents` → zod `AgentCreateSchema`; обяз. `name`,`provider`; `ollamaMode` только `local|cloud|null` (не `off`). - `POST /api/credentials` → `{provider,name,apiKey}`. - `POST /api/gateways` → `{name,endpoint,credentialId,isDefault}`. - `POST /api/openclaw/gateway` → RPC `{method:"gateway.connect",params:{url,token}|{profileId}}`. - БД: `/opt/swarmclaw/data/swarmclaw.db` (sqlite, таблицы `*_configs/profiles/agents/credentials` как `id+data JSON`), `memory.db`, `logs.db`. ## Домен swarm.dttb.ru (2026-06-08) - **NPM proxy host #32** (LXC 103, 10.0.0.195): `swarm.dttb.ru` → `10.0.0.135:3456`, WSS on, block_exploits. - **DNS:** wildcard `*.dttb.ru` есть только во **внутреннем** DNS (роутер) — http в LAN/NetBird заработал сразу. Публично wildcard НЕТ (каждый поддомен = отдельная A-запись в SpaceWeb). Добавил `swarm A 176.62.183.186` через [[../snippets/spaceweb-dns-api]] (`add-a`, **один вызов** — зона сверена до/после, 23→24 записи, ничего не задето; бэкап `/tmp/dttb_zone_before.txt`). - **HTTPS/LE — ГОТОВО:** DNS пропагировался за ~90 сек, LE cert выпущен (NPM cert **id 118**, до 2026-09-06), `ssl_forced` on, проверено `https://swarm.dttb.ru/api/healthz → 200`. Грабля NPM API: LE cert принимает только `meta:{}` (с полями → "additional properties"); email/agree NPM подставляет сам. Challenge падал, пока DNS не пропагировался — не из-за meta. - **Итог доступа:** `https://swarm.dttb.ru` (публично, за NPM+auth) и `http://swarm.dttb.ru` (LAN/NetBird). Вход — cookie `sc_auth` / ACCESS_KEY. ## TODO / опционально - Снести Mac-app `SwarmClaw.app` (или оставить как клиент). - Маппинг openclaw starter-агентов на gateway (роли/теги) — через UI. - Самообучение SwarmClaw (`conversation-to-skill`) — **полуручное** (draft→approve), не автономное; для настоящего автообучения остаётся openclaw Dreaming. ## Связано - [[openclaw]] — основной бот (LXC 137), теперь под управлением SwarmClaw - [[2026-05-06-openclaw-opus-4-7-via-max-cliproxy]]