Compare commits
25 Commits
3220238c67
...
claude/gre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a1b07a47e | ||
|
|
4e88741d08 | ||
| 97841320f2 | |||
|
|
d00d856513 | ||
| 329a280cb3 | |||
|
|
6dabc7749c | ||
| d82531ec58 | |||
|
|
47394e668e | ||
| 94aae3ca26 | |||
|
|
1748562756 | ||
|
|
f0b7feadc1 | ||
|
|
d4433bd0a8 | ||
|
|
b16ecdae37 | ||
|
|
b411e3b308 | ||
|
|
cda539b9a1 | ||
|
|
bac376992d | ||
|
|
f7d06c0a35 | ||
|
|
843b9780c8 | ||
|
|
24fe1d3f88 | ||
|
|
1ae613b2bd | ||
|
|
d754de8378 | ||
|
|
80fd8ca7bf | ||
|
|
bf845e2dcb | ||
| d9c00c31e0 | |||
| f6bf12ccf9 |
37
audit/2026-05-06-objects-audit.md
Normal file
37
audit/2026-05-06-objects-audit.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: audit
|
||||
source: scripts/kb-objects-audit.py
|
||||
tags: [audit, objects, frontmatter, links]
|
||||
score: 6
|
||||
---
|
||||
|
||||
# KB objects audit — 2026-05-06
|
||||
|
||||
**Score (меньше = лучше): `6`**
|
||||
|
||||
- Проектов с frontmatter: **12/12** (0 проблем)
|
||||
- NetBird online-пиров без проектной карточки: **3**
|
||||
- Битых wiki-ссылок `[[...]]`: **0**
|
||||
|
||||
## Frontmatter в projects/
|
||||
|
||||
✅ все проекты имеют валидный frontmatter
|
||||
|
||||
## Online netbird-пиры без проектной карточки
|
||||
|
||||
Эти пиры онлайн в NetBird, но не привязаны ни к одной projects/-странице.
|
||||
Бот не сможет ответить «найди X» осмысленно — нет файла или alias.
|
||||
|
||||
Лечение: либо создать stub в `projects/<slug>/README.md` (см. `projects/lipki/` как образец),
|
||||
либо добавить имя пира как полную строку в `aliases` подходящего проекта.
|
||||
|
||||
| NetBird-имя | IP | OS | Город |
|
||||
|---|---|---|---|
|
||||
| `DESKTOP-2IOQS54` | 100.70.82.83 | Windows 10 | Saransk |
|
||||
| `DESKTOP-AGBMLPN` | 100.70.0.106 | Windows 11 | Helsinki |
|
||||
| `DESKTOP-HL0BB05` | 100.70.235.80 | Windows 11 | Lipetsk |
|
||||
|
||||
## Битые wiki-ссылки
|
||||
|
||||
✅ битых ссылок не найдено
|
||||
1024
audit/objects-map.json
Normal file
1024
audit/objects-map.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,21 +4,20 @@ description: OmniRoute v3.6.5 на LXC 132 (10.0.0.179) — AI-шлюз для 6
|
||||
type: project
|
||||
originSessionId: 9f26284f-db92-456f-813d-fd8210b7d7b6
|
||||
---
|
||||
## OmniRoute v3.6.9+ (LXC 132, 10.0.0.179)
|
||||
## OmniRoute v3.6.5 (LXC 132, 10.0.0.179)
|
||||
|
||||
- **Путь**: /root/OmniRoute/, Node.js 22, Next.js 16.2, git repo (github.com/diegosouzapw/OmniRoute)
|
||||
- **Dashboard**: http://10.0.0.179:20128, пароль: 1qaz!QAZ
|
||||
- **API**: http://10.0.0.179:20128/v1 (или https://ai.dttb.ru/v1 публично через NPM с Bearer)
|
||||
- **API Key**: `sk-225e902dc95ff192-6bdad7-3ec8cdc6` (test-key) / `sk-225e902dc95ff192-e71c19-7cc9283e` (claw)
|
||||
- **API**: http://10.0.0.179:20128/v1
|
||||
- **API Key**: sk-225e902dc95ff192-6bdad7-3ec8cdc6
|
||||
- **SQLite DB**: /root/.omniroute/storage.sqlite
|
||||
- **Service**: omniroute.service (systemd, enabled), запускает **npx-кэш** (НЕ git repo)
|
||||
- **ExecStart**: `/usr/bin/node /root/.npm/_npx/<hash>/node_modules/omniroute/app/server.js` (hash меняется при обновлении)
|
||||
- **Конфиг**: /root/OmniRoute/.env (секреты сгенерированы)
|
||||
- **Каталог моделей**: 145+ моделей (на 2026-05-06)
|
||||
|
||||
### SSH доступ к LXC 132
|
||||
- **Прямой SSH** `ssh root@10.0.0.179` — РАБОТАЕТ если на машине есть claude-code ключ (на Mac Олега есть)
|
||||
- **Через Proxmox**: `sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 132 -- bash -c 'COMMAND'"` — РАБОТАЕТ всегда
|
||||
- **Прямой SSH (root@10.0.0.179) НЕ РАБОТАЕТ** — пароль 1qaz!QAZ не подходит, ключ claude-code отсутствует
|
||||
- **Доступ через Proxmox**: `sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 132 -- bash -c 'COMMAND'"` — РАБОТАЕТ
|
||||
- **Hostname**: code-server
|
||||
|
||||
### Обновление OmniRoute (npx способ — используется сервисом)
|
||||
@@ -31,34 +30,18 @@ originSessionId: 9f26284f-db92-456f-813d-fd8210b7d7b6
|
||||
### Обновление git-репозитория (отдельно, для разработки)
|
||||
1. `cd /root/OmniRoute && git pull origin main && npm install && npm run build`
|
||||
|
||||
### Подключённые провайдеры (актуально 2026-05-06)
|
||||
| Префикс | Провайдер | Модели Opus | Статус |
|
||||
|---------|-----------|-------------|--------|
|
||||
| **`cc/`** | Claude Code OAuth (Max-подписка) | **claude-opus-4-7**, opus-4-6, sonnet-4-6, sonnet-4-5, haiku-4-5 | ✅ Primary path к Opus |
|
||||
| `claude/` | прямой Anthropic API (платный) | claude-opus-4-7, opus-4-6, sonnet-4-6, haiku-4-5 | ✅ Резерв per-token |
|
||||
| `kr/` | Kiro/AWS Bedrock | sonnet-4.5 (Opus НЕТ) | ✅ для Sonnet, ❌ Opus |
|
||||
| `cx/` | Codex (OpenAI free) | gpt-5.4, gpt-5.4-mini, gpt-5.5 | ✅ для GPT |
|
||||
| `gh/` | GitHub Copilot integrator (MS) | sonnet-4.5/4.6, ~~opus-4.6~~, ~~opus-4.7~~ | ⚠️ Sonnet работает, Opus сломан (см. ниже) |
|
||||
| `antigravity/` | Google Antigravity | opus-4-6-thinking | ❌ Нужен OAuth reconnect (projectId пуст) |
|
||||
| `kc/` `kilocode/` | KiloCode aggregator | opus-4.7 | ❌ Empty response |
|
||||
| `glm/` | Zhipu Z.ai | glm-5.1 | ✅ |
|
||||
|
||||
### Главный путь к Opus 4.7 — `omniroute/cc/claude-opus-4-7`
|
||||
|
||||
Через **CLIProxy + Claude Code OAuth + Max-подписку**. Фиксированная стоимость, без per-token. Подтверждено 2026-05-06: smoke-test вернул корректный ответ от `claude-opus-4-7`. См. [[../decisions/2026-05-06-openclaw-opus-4-7-via-max-cliproxy]].
|
||||
|
||||
### Известные сломы
|
||||
|
||||
- **`gh/claude-opus-4.6`**: MS Copilot integrator убрал Opus 4.6 из своего scope, отдаёт «model not available for integrator vscode-chat»
|
||||
- **`gh/claude-opus-4.7`**: ID mismatch — OmniRoute шлёт `4.7`, MS ждёт `4-7` (точка vs дефис). Баг текущей версии OmniRoute. Workaround: использовать `cc/claude-opus-4-7` пока не починят.
|
||||
- **`antigravity/*`**: возвращает `"Missing Google projectId for Antigravity account. Please reconnect OAuth in Providers → Antigravity"`. Триггер из [[user_profile]]: до OAuth нужен вход на antigravity.google и создание Cloud Code project.
|
||||
### Подключённые провайдеры (2026-04-11)
|
||||
| Провайдер | Аккаунт | Модели | Статус |
|
||||
|-----------|---------|--------|--------|
|
||||
| Codex (OpenAI) | batlaew@gmail.com (free plan) | gpt-5.4, gpt-5.4-mini | Работает, refresh проблема |
|
||||
| Claude | OAuth | claude-opus-4-6, sonnet | Токен есть, refresh проблема |
|
||||
| Kiro (AWS) | OAuth | claude-sonnet-4.5, haiku | Токен есть, refresh проблема |
|
||||
|
||||
### Использование
|
||||
|
||||
- Prefix модели в API: `cc/claude-opus-4-7`, `kr/claude-sonnet-4.5`, `cx/gpt-5.4`
|
||||
- Codex OAuth callback: localhost:1455 (нужен SSH-туннель для удалённого онбординга)
|
||||
- Token refresh: периодическая перелогинка OAuth — проверять Dashboard → Providers
|
||||
- Health-check moniter: на LXC 132 в `journalctl -u omniroute -f` смотреть USAGE/STREAM строки
|
||||
- Prefix модели: `cx/gpt-5.4` (Codex), `cc/claude-opus-4-6` (Claude)
|
||||
- 181 модель доступна через API (на 2026-04-14)
|
||||
- Codex OAuth: callback на localhost:1455 (нужен SSH-туннель для удалённого доступа)
|
||||
- Token refresh: требует периодическую перелогинку OAuth
|
||||
|
||||
### Важно
|
||||
- OAuth Codex привязан к localhost:1455 — для удалённого доступа: `ssh -L 20128:localhost:20128 -L 1455:localhost:1455 root@10.0.0.179`
|
||||
|
||||
@@ -12,7 +12,7 @@ tags: [decision, niikn, network, dns, podkop]
|
||||
|
||||
## Диагноз
|
||||
|
||||
По алгоритму [[notes/govru-diagnosis]]:
|
||||
По алгоритму [[../projects/niikn/govru-quickfix-playbook]]:
|
||||
|
||||
| Точка | HTTP | Real IP |
|
||||
|-------|------|---------|
|
||||
|
||||
@@ -117,7 +117,7 @@ RustDesk при первом запуске парсит своё имя фай
|
||||
|
||||
## TODO / следующие шаги
|
||||
|
||||
- [ ] Олег создаёт public share-link в Nextcloud (с паролем), кладёт ссылку в [[../snippets/clients/]]
|
||||
- [ ] Олег создаёт public share-link в Nextcloud (с паролем), кладёт ссылку в `snippets/clients/`
|
||||
- [ ] Подготовить **one-liner-скрипты** на сервере (NPM static path `/install/` → S3 или rustdesk-api resources/public)
|
||||
- [ ] **Configuration Strategy** в админке lejianwen-api — push config existing peers
|
||||
- [ ] Скачать `rustdesk-utils` 2.x (lejianwen-pro) для генерации `rustdesk-licensed-*.exe` — single-file deployment без скриптов
|
||||
|
||||
@@ -12,7 +12,7 @@ tags: [decision, niikn, network, dns, podkop]
|
||||
|
||||
## Диагноз
|
||||
|
||||
По алгоритму [[notes/govru-diagnosis]]:
|
||||
По алгоритму [[../projects/niikn/govru-quickfix-playbook]]:
|
||||
|
||||
| Точка | HTTP | Real IP |
|
||||
|-------|------|---------|
|
||||
|
||||
@@ -75,6 +75,6 @@ ssh root@10.0.0.1 "cp /etc/adguardhome.yaml.bak.20260430_001129 /etc/adguardhome
|
||||
## Ссылки
|
||||
|
||||
- [[../projects/dttb/openwrt-router]] — карточка роутера
|
||||
- [[../claude-memory/podkop]] — справка по подкопу (схемы AGH, fakeip 198.18.0.0/15)
|
||||
- [[2026-04-17-peredelki-podkop-stability-fix]] — справка по подкопу (схемы AGH, fakeip 198.18.0.0/15)
|
||||
- https://podkop.net/docs/adguard/
|
||||
- https://podkop.net/docs/dont-touch-my-dhcp/
|
||||
|
||||
@@ -148,4 +148,4 @@ Grizzlysms TJ-pool **полностью забанен** Apple на текущи
|
||||
|
||||
- [[../README]] — индекс vault
|
||||
- TODO в MEMORY был «отложено с 2026-04-23», теперь активирован.
|
||||
- Решение по [[../snippets/clients/]] для US Apple ID на RU iPhone — структурно тот же подход (proxy + foreign SMS).
|
||||
- Решение по `snippets/clients/` для US Apple ID на RU iPhone — структурно тот же подход (proxy + foreign SMS).
|
||||
|
||||
101
decisions/2026-05-06-kb-search-overhaul.md
Normal file
101
decisions/2026-05-06-kb-search-overhaul.md
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: decision
|
||||
tags: [kb, openclaw, search, fts, infra, summary]
|
||||
status: applied
|
||||
---
|
||||
|
||||
# Перестройка KB-поиска и синхронизации (8 фаз за один день)
|
||||
|
||||
## Что было до
|
||||
|
||||
Запрос «OpenWRT Липки» Максимке возвращал `netbird-inventory.md`, потому что:
|
||||
- слова «Липки» во всём vault'е была одна строка в табличке инвентаря,
|
||||
- слово «OpenWRT» статистически перевешивали НИИКН-документы (`govru-quickfix`, `niikn-openwrt-awg-fix`),
|
||||
- кросс-ссылок и aliases не было, embeddings не было,
|
||||
- лаг push→бот доходил до 16 минут (cron `*/5` Mac→Gitea + cron `*/15` Gitea→openclaw).
|
||||
|
||||
Пользователь жаловался: «бот путает, я ему про Липки, он мне про НИИКН».
|
||||
|
||||
## Что сделано
|
||||
|
||||
| Фаза | Эффект |
|
||||
|---|---|
|
||||
| 1. Embeddings (ollama+bge-m3) | ⛔ откачено — openclaw 2026.5.2 при `provider: ollama` шлёт inputs целиком, ollama тратит >5 мин/запрос, undici fetch валится по headers timeout. ollama на LXC 132 готова (uptime, bge-m3 1024d), но сторона openclaw нуждается в фиксе chunking |
|
||||
| 2. Webhook + auto-reindex | Gitea push→listener (HMAC SHA-256)→`kb-pull.sh`→`openclaw memory index`. **push→FTS: 15 мин → 50 c**. См. [[../snippets/openclaw-kb-webhook]] и [[2026-05-06-openclaw-kb-webhook-deployment]] |
|
||||
| 3. objects-map.json + _index.md | Скрипт `scripts/kb-objects-map.py` парсит netbird-inventory + frontmatter и пишет JSON для бота + human-readable [[../projects/_index]]. Базовый структурный слой |
|
||||
| 4. Stubs + frontmatter | Создано 6 README (`znamenskoye`, `mmfb`, `lipki` уже был, + 4 stub'а клиентских OpenWrt). Обогащён frontmatter в `niikn`, `dttb`, `glavtorg`, `krasnogorsk`, `zelenograd` с aliases для netbird-имён. **38 → 14 orphan-пиров** |
|
||||
| 5.1. Слить дубли видео | Удалён `dttb/video-surveillance-report.md` (297 строк, неполная копия), canonical `videonablyudenie-znam.md` обогащён aliases |
|
||||
| 5.2. Архив audit/ | 18 устаревших drift-отчётов 2026-04-* перенесены в `audit/archive/`, в active остались 7 свежих |
|
||||
| 6. Recall + dreaming | `openclaw memory promote --apply`: 17/39 коротких воспоминаний переехали в долгую. `dreaming.enabled=true` (cron `0 3 * * *`). Weekly cron на promote |
|
||||
| 7. Расширенный kb-audit | Скрипт `scripts/kb-objects-audit.py` пишет еженедельный отчёт с score: проверяет frontmatter, online-orphans, битые wiki-ссылки. Первый запуск — score=84 (12/12 frontmatter ✓, 3 online orphan, 26 битых wiki) |
|
||||
| 8. UX бота | `memorySearch.query.minScore=0.4`, IDENTITY.md разводит двух Максимок (homelab vs НИИКН), INFRASTRUCTURE.md переписан как навигатор по vault'у |
|
||||
|
||||
## Метрики до / после
|
||||
|
||||
| Параметр | До | После |
|
||||
|---|---|---|
|
||||
| «OpenWRT Липки» top-1 | `netbird-inventory.md` ❌ | `projects/lipki/README.md` ✅ |
|
||||
| «Антон клиент Звенигород» top-1 | ничего | `projects/lipki/README.md` ✅ |
|
||||
| «AgentDVR ЧОП Знаменское» top-1 | три дубля | `videonablyudenie-znam.md` ✅ |
|
||||
| «Константин Вишневый сад» top-1 | `netbird-inventory.md` | `projects/vishnevyy-sad/README.md` ✅ |
|
||||
| Лаг push → видно боту | 5–16 мин | **~50 секунд** |
|
||||
| Проектов с netbird-привязкой | 0 | **9** |
|
||||
| Структурный объект-граф | нет | `objects-map.json` (34 entries) |
|
||||
| Online orphan-пиров | ~30+ | **3** |
|
||||
| Auto-reindex после pull | нет | есть |
|
||||
| Recall promoted | 0 (mёртв 16 дней) | 17 + weekly cron |
|
||||
| Dreaming | off | on (ночной cron) |
|
||||
| FTS-шум audit/ | 24 файла | 7 active + 18 в archive |
|
||||
|
||||
## Грабли (для будущей памяти)
|
||||
|
||||
1. **Gitea SSRF-protection.** Без `webhook.ALLOWED_HOST_LIST` в `app.ini` Gitea silently отказывает доставлять webhook на private IP. В docker-логах: `webhook can only call allowed HTTP servers, deny '10.0.0.239'`. Listener при этом получает 0 запросов, journal пустой.
|
||||
|
||||
2. **`flock -n` теряет двойные push.** Если webhook прилетел во время pull/reindex — non-blocking lock молча выходит. Заменить на `flock -w 180`.
|
||||
|
||||
3. **openclaw 2026.5.2 ollama-провайдер игнорирует chunking.** При `provider: ollama` шлёт файлы целиком в `/api/embed`, ollama buffer растёт до 700 MB, обработка >5 мин, fetch валится. Не лечится `chunking.tokens=512`, `nonBatchConcurrency=1`, `batch.enabled=false`. Bug либо в openclaw, либо неочевидный конфиг-флаг.
|
||||
|
||||
4. **substring matching → false positives.** Alias `cloud` подхватывал `Cloud-NIIKN New niikn.com`. Перешли на exact-match с нормализацией `ye→e`. Aliases теперь должны содержать **полные** имена пиров как в netbird-inventory.
|
||||
|
||||
5. **Heredoc через ssh с backticks** — bash интерпретирует backticks как command-substitution внутри пути файла. Для записи структурных markdown'ов с inline-кодом — пушить через base64 или scp/pct push, не heredoc.
|
||||
|
||||
6. **`git commit` без `-a` или явного `git add`** забирает только staged. Удаление через `git rm` помечается автоматом, а modify — нет. Один коммит может оставить часть правок несинхронизированными — дополнительный коммит лечит.
|
||||
|
||||
## Что отложено
|
||||
|
||||
| Задача | Причина | Когда возвращаться |
|
||||
|---|---|---|
|
||||
| Векторный поиск через ollama | bug в openclaw 2026.5.2 ollama-провайдере | при апгрейде openclaw, или попробовать `provider: local` (transformers.js встроен в openclaw) |
|
||||
| 14 orphan-пиров (Денис Тихая, DESKTOP-2IOQS54 Saransk, KOMPUTER, MastaNotebook, …) | нужны факты от Олега чьи это машины | по мере выяснения — добавлять в aliases подходящих проектов |
|
||||
| 26 битых wiki-ссылок | автоматический фикс не всегда возможен (некоторые цели реально удалены) | semi-auto проход: показать список, для очевидных — починить, остальное — оставить TODO |
|
||||
| Singleton-проекты (`projects/*.md`) без frontmatter | не критично — они top-level note'ы | при касании |
|
||||
|
||||
## Артефакты в vault
|
||||
|
||||
- [[../projects/lipki/README]], [[../projects/znamenskoye/README]], [[../projects/mmfb/README]]
|
||||
- [[../projects/sergey/README]], [[../projects/benilux/README]], [[../projects/vishnevyy-sad/README]], [[../projects/openwrt-4/README]]
|
||||
- [[../projects/_index]] — авто-генерированный реестр
|
||||
- `audit/objects-map.json` — машинно-читаемый граф
|
||||
- [[../audit/2026-05-06-objects-audit]] — первый health-check
|
||||
- `scripts/kb-objects-map.py`, `scripts/kb-objects-audit.py`
|
||||
- [[../snippets/openclaw-kb-webhook]]
|
||||
- [[2026-05-06-openclaw-kb-webhook-deployment]]
|
||||
- frontmatter добавлен в `projects/{niikn,dttb,glavtorg,krasnogorsk,zelenograd}/README.md`
|
||||
|
||||
## Артефакты вне vault
|
||||
|
||||
**LXC 137 (openclaw):**
|
||||
- `/usr/local/bin/kb-pull-webhook.py` (Python listener, HMAC)
|
||||
- `/etc/systemd/system/kb-pull-webhook.service`
|
||||
- Обновлённый `/usr/local/bin/kb-pull.sh` (`flock -w 180`, hash-diff, auto-reindex)
|
||||
- Cron `0 4 * * 1` weekly memory promote
|
||||
- Config: `memorySearch.query.minScore=0.4`, `dreaming.enabled=true`, ollama-провайдер откачен (бэкап `/root/.openclaw/openclaw.json.bak.embed-2026-05-06-112441`)
|
||||
- Переписаны `/root/clawd/IDENTITY.md`, `INFRASTRUCTURE.md`
|
||||
|
||||
**LXC 136 (Gitea):**
|
||||
- `webhook.ALLOWED_HOST_LIST = 10.0.0.0/24,*.dttb.ru,private` в `/opt/gitea/data/gitea/conf/app.ini`
|
||||
- Webhook id=1 на репо `oleg/knowledge-base`
|
||||
|
||||
**LXC 132 (code-server):**
|
||||
- ollama systemd override `OLLAMA_HOST=0.0.0.0:11434, KEEP_ALIVE=24h` (для будущего возврата к embeddings)
|
||||
78
decisions/2026-05-06-openclaw-kb-webhook-deployment.md
Normal file
78
decisions/2026-05-06-openclaw-kb-webhook-deployment.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: decision
|
||||
tags: [openclaw, gitea, webhook, kb-sync, performance]
|
||||
status: applied
|
||||
---
|
||||
|
||||
# Webhook Gitea → openclaw kb-pull (Фаза 2 плана улучшения KB-поиска)
|
||||
|
||||
## Контекст
|
||||
|
||||
После публикации `projects/lipki/README.md` стало видно, что openclaw (Максимка) видит новые правки vault только через cron `*/15` — лаг до 15 минут. Это первое, что бьёт по UX («только что записал → бот всё ещё не знает»).
|
||||
|
||||
План улучшения KB-поиска расписан в чате с Claude Code 2026-05-06 утром, Фаза 2 — webhook + автоматический FTS-реиндекс.
|
||||
|
||||
## Решение
|
||||
|
||||
Push-канал Gitea → listener на LXC 137 → `kb-pull.sh` → `openclaw memory index`. Cron `*/15` оставлен как safety net.
|
||||
|
||||
Подробная реализация — [[../snippets/openclaw-kb-webhook]].
|
||||
|
||||
## Что развёрнуто
|
||||
|
||||
| Компонент | Где |
|
||||
|---|---|
|
||||
| `/usr/local/bin/kb-pull-webhook.py` | LXC 137, Python http.server, HMAC-SHA256 проверка, journal-лог |
|
||||
| `/etc/systemd/system/kb-pull-webhook.service` | LXC 137, system unit, Restart=on-failure, secret в env |
|
||||
| Обновлённый `/usr/local/bin/kb-pull.sh` | `flock -w 180`, hash-diff, авто `openclaw memory index` при новом HEAD |
|
||||
| Webhook в Gitea (id=1, репо `oleg/knowledge-base`) | events: push, branch: main, secret в HMAC |
|
||||
| `webhook.ALLOWED_HOST_LIST = 10.0.0.0/24,*.dttb.ru,private` в `app.ini` | LXC 136 (Gitea в docker), `/opt/gitea/data/gitea/conf/app.ini` |
|
||||
|
||||
## Метрика
|
||||
|
||||
End-to-end push → видно боту:
|
||||
|
||||
| Этап | Время |
|
||||
|---|---|
|
||||
| `git push` с Mac → Gitea ack | ~2 c |
|
||||
| Gitea → webhook listener | ~3 c |
|
||||
| listener → kb-pull.sh → git pull → новый HEAD | ~6 c |
|
||||
| **Итого до видимости HEAD на 137** | **~11 c** |
|
||||
| FTS reindex (875 файлов / 1791 chunk) | +38 c |
|
||||
| **Итого до видимости в `openclaw memory search`** | **~50 c** |
|
||||
|
||||
Было: до 15 минут (cron */15) + до 38 c reindex (если бы он раньше был) = до **~16 минут**.
|
||||
|
||||
Улучшение: **×20 латентности до видимости HEAD, ×16 до полного FTS**.
|
||||
|
||||
## Грабли (для будущей памяти)
|
||||
|
||||
1. **Gitea SSRF-protection.** Без `ALLOWED_HOST_LIST` Gitea silently отказывает доставлять webhook на private IP. Test delivery возвращает `204`, но в Gitea log: `webhook can only call allowed HTTP servers, deny '10.0.0.239'`. Listener при этом не видит ничего — пустой journal. Это была первая причина почему ничего не работало.
|
||||
|
||||
2. **`flock -n` теряет вторую серию push'ов.** Если webhook прилетел во время уже идущего pull/reindex (быстрый двойной push) — non-blocking flock молча выходит. Надо `-w 180` (ждать в очереди до 3 мин).
|
||||
|
||||
3. **sed по строке с `||` ломается, если разделитель `|`.** `sed -i 's|flock -n|flock -w 180|'` падает с `unknown option to s`. Использовать `#` или другой разделитель — `sed -i 's#flock -n#flock -w 180#'`.
|
||||
|
||||
4. **Listener `log_message: pass` гасит **все** логи http.server.** Если хочется видеть приём POST — переопределить как `sys.stderr.write(...)`. Иначе debug невозможен.
|
||||
|
||||
## Альтернативы, которые отвергнуты
|
||||
|
||||
- **Полностью убрать cron**: рискованно — если listener умер и не заметили, vault зависнет. Cron `*/15` ничего не стоит и страхует.
|
||||
- **Webhook напрямую через NPM/публичный URL**: лишний хоп, нужен HTTPS, всё в LAN — не нужно.
|
||||
- **iptables на 18790**: HMAC-secret уже отсекает левые запросы. Homelab trusted. Не делал.
|
||||
|
||||
## Откат
|
||||
|
||||
См. секцию «Откат» в [[../snippets/openclaw-kb-webhook]]. Один rm-рецепт + удаление webhook через Gitea API.
|
||||
|
||||
## Что дальше
|
||||
|
||||
Фаза 1 плана (embeddings ollama × bge-m3) — пока **отложена**. При попытке развёртывания openclaw 2026.5.2 со всеми правильными config-полями (`provider=ollama`, `remote.baseUrl`, `model=bge-m3`, `chunking.tokens=512`) **игнорирует chunking** и шлёт в `/api/embed` файлы целиком — output buffer ollama растёт до 700+ MB, обработка >5 мин, undici fetch падает по дефолтному 5-min Headers Timeout. Конфигурация откачена, состояние FTS-only сохранено.
|
||||
|
||||
Возможные пути возврата к векторному поиску:
|
||||
- `provider: local` (transformers.js встроенный) — попробовать без сетевого хопа
|
||||
- Ждать openclaw 2026.5.3+ с фиксом chunking
|
||||
- GitHub Copilot embeddings (требует токен)
|
||||
|
||||
Следующая фаза по плану — **Фаза 3** (`audit/objects-map.json` + `projects/_index.md`) или **Фаза 5.1** (слить дубли видеонаблюдения).
|
||||
@@ -116,3 +116,4 @@ sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 137 -- bash -c '
|
||||
- [[../claude-memory/omniroute]] — OmniRoute setup, провайдеры, версии
|
||||
- [[../projects/dttb/openclaw]] — справочник по openclaw
|
||||
- [[../snippets/clawdbot-cliproxy-config]] — старый шаблон CLIProxy
|
||||
- [[../snippets/omniroute-models-audit]] — шаблон smoke-тестов и парсинга ошибок (использовался при этом аудите)
|
||||
|
||||
90
decisions/2026-05-07-buzharovo-1c-rmngr-loop-after-crash.md
Normal file
90
decisions/2026-05-07-buzharovo-1c-rmngr-loop-after-crash.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
date: 2026-05-07
|
||||
type: decision
|
||||
tags: [buzharovo, server1c, 1c, troubleshooting]
|
||||
---
|
||||
|
||||
# Бужарово 1С сервер — rmngr-loop после грязного ребута: рецепт
|
||||
|
||||
## Контекст
|
||||
|
||||
07.05.2026 утром локальные пользователи в Бужарово начали жаловаться на резкое замедление 1С. NetBird и удалённый доступ ни при чём — тормозило в локальной сети офиса.
|
||||
|
||||
Сервер: `Server1C` (Win 2012 R2, 100.70.75.103 / 185.13.47.2), 1С 8.3.27.1606, MSSQL.
|
||||
|
||||
## Что нашли
|
||||
|
||||
Сегодня в **09:05 сервер ушёл в crash**:
|
||||
- `System / Kernel-Power Event 41` (Critical): "rebooted without cleanly shutting down — system stopped responding, crashed, or lost power"
|
||||
- `System / EventLog 6008`: "previous shutdown was unexpected"
|
||||
|
||||
После загрузки:
|
||||
- `rmngr.exe` (менеджер кластера 1С) держит **~1.7 ядра постоянно** в idle (норма <2% одного ядра).
|
||||
- `rphost`, `sqlservr`, диски (avg sec/write 1.6 ms, queue 0.03), память (5/64 GB) — **в норме**.
|
||||
- `rac.exe localhost:1540 cluster list` → "ошибка соединения с сервером", exit -1 — **admin-канал rmngr повис**, кластером невозможно управлять извне.
|
||||
- `netbird.exe` параллельно жжёт 1.4 ядра — побочный reconnect-loop после crash, после ребута сам успокоился.
|
||||
|
||||
## Что не помогло
|
||||
|
||||
**Полный ребут сервера** (через `Restart-Computer`) — НЕ решает проблему. На свежезагруженном сервере с uptime=37 секунд rmngr опять на 178% одного ядра. То есть проблема воспроизводится при каждом первом запуске агента после crash.
|
||||
|
||||
## Что помогло
|
||||
|
||||
```powershell
|
||||
Restart-Service -Name '1C:Enterprise 8.3 Server Agent (x86-64)' -Force
|
||||
```
|
||||
|
||||
После рестарта службы (не сервера!):
|
||||
|
||||
| Процесс | До рестарта | После рестарта |
|
||||
|---|---|---|
|
||||
| rmngr | 178% ядра | 3% ядра |
|
||||
| rphost | 19% | 1% |
|
||||
| ragent | 0% | 1% |
|
||||
|
||||
Все клиенты в этот момент вылетают — нужно предупредить за 2-3 минуты. Перезаход штатный.
|
||||
|
||||
## Побочный эффект
|
||||
|
||||
После рестарта остался орфан-процесс старого `ragent` от прошлого запуска (не слушает 1540, висит в памяти). Можно безопасно прибить:
|
||||
|
||||
```powershell
|
||||
Get-Process ragent | Where-Object { $_.Id -ne (Get-NetTCPConnection -LocalPort 1540).OwningProcess } | Stop-Process -Force
|
||||
```
|
||||
|
||||
## Корневая причина — открыта
|
||||
|
||||
Почему `rmngr` зацикливается после первого запуска ragent — точно не выяснено. Гипотезы:
|
||||
1. Повреждённый кэш кластера `C:\Program Files\1cv8\srvinfo\reg_*\` после crash.
|
||||
2. Регресс в 8.3.27.1606 при восстановлении сеансов.
|
||||
3. Disabled-служба `RagentServer_8327` (старый дубликат) что-то мешает в реестре при первом старте.
|
||||
|
||||
Если повторится — смотреть `C:\Program Files\1cv8\srvinfo\reg_*\1Cv8FTLog\` и `1Cv8Log` на ошибки.
|
||||
|
||||
## Что сделать долгосрочно
|
||||
|
||||
В кластере 1С сейчас **один rphost на всех локальных пользователей** — бутылочное горлышко. Через консоль администрирования сервера 1С увеличить количество рабочих процессов: 1 rphost на 8-12 сеансов. Это отдельная задача, не на горячую.
|
||||
|
||||
## Команды диагностики (для повтора)
|
||||
|
||||
WinRM-сессия с Mac:
|
||||
```python
|
||||
import winrm
|
||||
s = winrm.Session('http://100.70.75.103:5985/wsman',
|
||||
auth=('dttb','1qaz!QAZ'),
|
||||
transport='basic')
|
||||
```
|
||||
|
||||
Дельта CPU за 5 сек по процессам:
|
||||
```powershell
|
||||
$names='rmngr','rphost','netbird','sqlservr','ragent'
|
||||
$first=@{}; $last=@{}
|
||||
Get-Process | ?{$names -contains $_.Name} | %{ $first[$_.Name]=$_.CPU }
|
||||
Start-Sleep 5
|
||||
Get-Process | ?{$names -contains $_.Name} | %{ $last[$_.Name]=$_.CPU }
|
||||
foreach($n in $names){ "$n delta=$($last[$n]-$first[$n])s" }
|
||||
```
|
||||
|
||||
Если у rmngr дельта >50 за 5 секунд — диагноз "rmngr-loop" подтверждён, делать рестарт службы.
|
||||
|
||||
См. также [[projects/buzharovo/server1c]].
|
||||
75
decisions/2026-05-08-buzharovo-sql-native-backup.md
Normal file
75
decisions/2026-05-08-buzharovo-sql-native-backup.md
Normal file
@@ -0,0 +1,75 @@
|
||||
---
|
||||
date: 2026-05-08
|
||||
type: decision
|
||||
tags: [decision, buzharovo, 1c, backup, mssql, effector-saver]
|
||||
---
|
||||
|
||||
# 2026-05-08: Бэкап Бужарово 1С — переход с Effector Saver DT на native MS SQL Backup
|
||||
|
||||
## Контекст
|
||||
|
||||
Вечером 2026-05-08, после починки rmngr-loop, регулярная задача `Бэкап 1Cv8` в Effector Saver Free 4.8/2 на `server1c.netbird.cloud` (Win 2012 R2, MSSQL 2012 SP4, 1С 8.3.27.1606) **отказывалась завершаться успешно**. Шесть подряд запусков (19:51 → 22:21) падали с одной и той же связкой ошибок:
|
||||
|
||||
1. `HRESULT=800401F3` — `V83.ComConnector` не зарегистрирован → Olег зарегистрировал через UI Effector Saver, переключился на 64-bit
|
||||
2. `HRESULT=80004005` — "Администратор кластера не аутентифицирован" — у кластера 1С есть проверка авторизации, но **в кластере нет ни одного admin'а**, а добавить нельзя:
|
||||
- В Серверной консоли 1С: `Локальный кластер → Администраторы` показывает количество=0, форма "Новый администратор" заполнена, но при OK **просит логин/пароль и не принимает agent-уровневый admin** (создан Olегом отдельно)
|
||||
- Через COM `V83.COMConnector`: `AuthenticateAgent('admin', '1qaz!QAZ')` проходит, но `RegClusterAdmin` падает "пользователь не выполнил аутентификацию для требуемой операции" (chicken-and-egg: для создания первого cluster admin нужен уже cluster admin)
|
||||
- Через `rac` с `--agent-user`: cluster operations не принимают agent-уровневую аутентификацию (design choice 1С)
|
||||
|
||||
3. `Ошибка исключительной блокировки информационной базы` — даже без cluster admin'а Effector Saver продолжает выгрузку, но не может получить эксклюзив, потому что в БД активные сессии. Особенно "вечная" сессия `КулябинПИ sid=4514, начат 12:55:42` — после моего рестарта службы 1С в 19:30 и последующих SQL `KILL` сессий, она **раз за разом возвращается** (вероятно, реально открытый где-то тонкий клиент Павла Ивановича + persistent state в `1CV8Clst.lst`).
|
||||
|
||||
## Что пробовали и почему не сработало
|
||||
|
||||
| Попытка | Результат |
|
||||
|---|---|
|
||||
| `regsvr32` x64 `comcntr.dll` | ✅ COM зарегистрирован, переключение Effector Saver на 64-bit убрало `800401F3` |
|
||||
| `Restart-Service '1C:Enterprise 8.3 Server Agent'` | ✅ rmngr вылечен, но session 4514 в реестре кластера **persists** между рестартами |
|
||||
| `KILL` SQL-сессий через `sa/Qwer1122334400` | ✅ временно (sess=0, locks=0), но 1С rphost восстанавливает соединения за 1-2 мин и сессия 4514 reanimates |
|
||||
| `rac cluster admin register` без auth | ❌ "оператор не существует" |
|
||||
| `RegClusterAdmin` через COM с `AuthenticateAgent` | ❌ "пользователь не аутентифицирован для требуемой операции" |
|
||||
| GUI Серверной консоли 1С — добавить cluster admin | ❌ форма не сохраняет, требует cluster auth (которой нет) |
|
||||
|
||||
Тупик: **в кластере БД 1С нет cluster admin'а, и зарегистрировать первого нельзя ни через GUI, ни через rac, ни через COM.** Возможный единственный путь — обнулить `srvinfo\reg_1541\1CV8Clst.lst` целиком (потеря и админов, и регистрации ИБ — нужна перерегистрация ИБ с SQL params; рискованно).
|
||||
|
||||
## Решение: native MS SQL Backup
|
||||
|
||||
Effector Saver делает **DT-выгрузку через 1С Конфигуратор** (`1cv8.exe DESIGNER /DumpIB`), которая требует эксклюзив на ИБ. Это исторический способ для **файловых** ИБ. Для **клиент-серверных** ИБ на MS SQL правильный путь — **`BACKUP DATABASE` на уровне SQL Server**:
|
||||
|
||||
- ✅ **Online backup** — снимает копию во время работы, не требует эксклюзива
|
||||
- ✅ Не зависит от 1С-кластера, cluster admin'а, активных сессий
|
||||
- ✅ С `WITH COMPRESSION` файл сжимается ~3:1 (3.8 GB → 1.1 GB)
|
||||
- ✅ Быстрее — у нас 2 секунды на 3.8 GB БД
|
||||
- ✅ Восстанавливается одним запросом `RESTORE DATABASE`
|
||||
|
||||
Реализация:
|
||||
|
||||
```sql
|
||||
BACKUP DATABASE [RitmUl]
|
||||
TO DISK = N'C:\backup\RitmUl_<ts>.bak'
|
||||
WITH FORMAT, INIT, NAME = N'RitmUl-Full',
|
||||
SKIP, NOREWIND, NOUNLOAD, COMPRESSION, COPY_ONLY,
|
||||
STATS = 5
|
||||
```
|
||||
|
||||
`COPY_ONLY` — чтобы наш бэкап не ломал log chain если потом настроят differential/log backups.
|
||||
|
||||
Запускается через WinRM `python3 + System.Data.SqlClient` с LXC 139 `severny-les` (бот). Storage: `C:\backup\` на server1c (374 GB свободно).
|
||||
|
||||
## Артефакт
|
||||
|
||||
- **Первый успешный бэкап 2026-05-08:** `C:\backup\RitmUl_2026-05-08_2225.bak` (1105 MB)
|
||||
- **Скрипт:** `/root/clawd/scripts/sql_native_backup.py` на LXC 139
|
||||
|
||||
## TODO (после возвращения Olега из Египта)
|
||||
|
||||
1. **Автоматизировать** — добавить cron на LXC 139 `severny-les`: каждый день в 03:00 МСК запускать `sql_native_backup.py`, ротировать (хранить N дней). Алерт в Telegram-группу при сбое.
|
||||
2. **Ротация и трансфер** — настроить копирование `.bak` файлов на внешний носитель (Nextcloud / S3 / Gitea-LFS).
|
||||
3. **Тест восстановления** — раз в N дней автоматически развернуть бэкап в тестовую БД и проверить целостность.
|
||||
4. **Effector Saver** оставить как есть, не чинить (чинить cluster admin = разбирать `1CV8Clst.lst` бинарник, риск убить ИБ). Можно отключить регулярную задачу `Бэкап 1Cv8` в Effector Saver чтобы не плодились алерты "ошибка".
|
||||
5. **TODO документировать** SQL creds в `projects/dttb/credentials.md` (см. блок Бужарово).
|
||||
|
||||
## Связанные
|
||||
|
||||
- [[projects/buzharovo/server1c]] — обновлён с SQL backup как новый канон
|
||||
- [[projects/buzharovo/severny-les-bot]] — бот теперь умеет бэкапить через WinRM+SQL
|
||||
- [[decisions/2026-05-07-buzharovo-1c-rmngr-loop-after-crash]] — про rmngr (отдельная история, починена)
|
||||
77
decisions/2026-05-08-severny-les-bot-buzharovo.md
Normal file
77
decisions/2026-05-08-severny-les-bot-buzharovo.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
date: 2026-05-08
|
||||
type: decision
|
||||
tags: [decision, buzharovo, bot, openclaw, watchdog, telegram]
|
||||
---
|
||||
|
||||
# 2026-05-08: Северный лес — отдельный AI-ассистент + watchdog для server1c (Бужарово)
|
||||
|
||||
## Контекст
|
||||
|
||||
Олег уезжает в отпуск в Египет 2026-05-09 → 2026-05-22. На server1c (Бужарово, VDS 185.13.47.2 / NetBird 100.70.75.103) недавно (2026-05-07) был rmngr-loop, который лечится только `Restart-Service '1C:Enterprise 8.3 Server Agent (x86-64)' -Force` — ребут не помогает (см. [[decisions/2026-05-07-buzharovo-1c-rmngr-loop-after-crash]]).
|
||||
|
||||
Пока Олег в отпуске, нужно:
|
||||
1. Чтобы кто-то узнавал когда сервер упал (Telegram-группа руководящего состава Северного леса);
|
||||
2. Чтобы можно было дёрнуть восстановительное действие (`/approve restart_1c`) не дожидаясь возвращения Олега из Египта.
|
||||
|
||||
Через ~2 недели (после Египта) планируется миграция server1c с VDS на собственный сервер. Бот должен работать **до и после** миграции — поэтому он не на самом server1c, а на dttb-Proxmox через NetBird.
|
||||
|
||||
## Развилка: clawdbot vs openclaw vs другой watchdog
|
||||
|
||||
Рассматривались три варианта:
|
||||
|
||||
| Вариант | Плюсы | Минусы |
|
||||
|---|---|---|
|
||||
| **clawdbot** (как у [[projects/niikn/clawdbot-niikn|Максимки-Мауля]]) | Проверенный рецепт, проще | Старый стек, не обновляется. exec-approvals самописные. |
|
||||
| **openclaw** (свежий стек 137) | Встроенный `exec-approvals.json` whitelist для shell-команд. Plugins, skills, делегирование на Opus 4.7 через Max. Свежий, активная разработка. | Жёсткая schema, есть тонкости (bonjour, IPv6, FakeIP DNS) — но они уже разобраны на 137. |
|
||||
| **Голый watchdog без AI** | Минимум зависимостей. | Нет диагностики "почему упало". Невозможно дёрнуть `/restart_1c` через `/approve` — только ручной WinRM. |
|
||||
|
||||
**Решение:** **openclaw** — встроенный whitelist для shell-команд (`exec-approvals.json`) — это прямо то что нужно для `/approve` flow. Плюс Опус 4.7 через Max.
|
||||
|
||||
## Архитектура
|
||||
|
||||
**Изоляция от Максимки (LXC 137):** не подвешиваем как доп.канал на 137 — если openclaw на 137 упадёт (а это бывает: bonjour, FakeIP, Kiro 402), упадут и алерты Бужарово. Для критичной мониторинг-задачи нужен **отдельный** инстанс.
|
||||
|
||||
**Хост:** новый LXC 139 на dttb (10.0.0.240, NetBird 100.70.212.78, Ubuntu 24.04, 2c/4GB/10GB).
|
||||
|
||||
**Два слоя независимых:**
|
||||
1. **buzharovo-watchdog** — bash + curl→TG bot API напрямую, systemd timer 60s. **Не зависит от openclaw.** Если AI-часть упала, алерт всё равно дойдёт.
|
||||
2. **openclaw 2026.5.7** — AI-помощник для диагностики и `/approve`-action'ов через WinRM.
|
||||
|
||||
**Алерт-уровни:**
|
||||
- `OK` — всё доступно;
|
||||
- `WARNING` — часть проверок упала;
|
||||
- `WARNING_NETBIRD` — NetBird до server1c лежит, публично сервер виден;
|
||||
- `CRITICAL` — сервер не отвечает ни публично, ни через NetBird.
|
||||
|
||||
Антиспам: алерт шлётся **только при смене уровня**, состояние в `/var/lib/severny-les/state.json`.
|
||||
|
||||
**WinRM-actions с подтверждением:**
|
||||
- read-only без approval (`/status`, `/check_1c`, `/check_rmngr`);
|
||||
- destructive с обязательным `/approve` от Олега `1292155421` (`/restart_1c`, `/kill_orphan_ragent`);
|
||||
- ребута сервера НЕ даём (по опыту 2026-05-07 не помогает rmngr-loop).
|
||||
|
||||
## Превентивные правки на старте (уроки 137)
|
||||
|
||||
Все три "ловушки openclaw" пропатчены сразу:
|
||||
1. `plugins.entries.bonjour.enabled = false` — против mDNS crash-loop (см. [[projects/dttb/openclaw#Crash-loop-каждые-40-сек]]).
|
||||
2. `pct set 139 --nameserver '1.1.1.1 8.8.8.8'` + правка `/etc/resolv.conf` — против FakeIP DNS от 10.0.0.1.
|
||||
3. `NODE_OPTIONS=--dns-result-order=ipv4first` в systemd unit — против IPv6-сбоев Telegram API.
|
||||
|
||||
systemd unit для openclaw — **system-level** (`/etc/systemd/system/openclaw-gateway.service`), а не `--user` как на 137. В LXC без user-session `systemctl --user` не работает (`Failed to connect to bus`).
|
||||
|
||||
## Что осталось сделать после возвращения Олега
|
||||
|
||||
1. **NetBird ACL** `severny-les` → `server1c` (порт 5985 TCP минимум) — без него WinRM-actions не работают, watchdog мониторит только публичные проверки.
|
||||
2. **Добавить @bz_sl_bot в TG-группу** руководящего состава, узнать `chat_id`, обновить `/etc/severny-les/watchdog.env` `BZ_TG_CHAT` и `openclaw.json` `groupAllowFrom`.
|
||||
3. После миграции server1c на свой сервер — обновить IP в `/root/clawd/INFRASTRUCTURE.md` и в `buzharovo-watchdog.sh`.
|
||||
|
||||
## Артефакты
|
||||
|
||||
- LXC 139 `severny-les` (10.0.0.240)
|
||||
- TG bot `@bz_sl_bot` (token `8322860033:...`)
|
||||
- Справочник: [[projects/buzharovo/severny-les-bot]]
|
||||
- Persona: `/root/clawd/{IDENTITY,INFRASTRUCTURE,USER,SOUL,TOOLS,MEMORY,HEARTBEAT}.md`
|
||||
- Скрипты: `/root/clawd/scripts/check_buzharovo.sh`, `winrm_lib.py`, `check_1c_service.py`, `check_rmngr_cpu.py`, `restart_1c_agent.py`, `kill_orphan_ragent.py`, `heartbeat.sh`
|
||||
- Watchdog: `/usr/local/bin/buzharovo-watchdog.sh` + `.service` + `.timer` (60s); `netbird-watchdog` clone с 137 (2 мин)
|
||||
- Whitelist: `/root/.openclaw/exec-approvals.json`
|
||||
85
decisions/2026-05-14-buzharovo-watchdog-public-only.md
Normal file
85
decisions/2026-05-14-buzharovo-watchdog-public-only.md
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
date: 2026-05-14
|
||||
type: decision
|
||||
tags: [decision, buzharovo, watchdog, netbird, monitoring, openclaw]
|
||||
---
|
||||
|
||||
# 2026-05-14: Watchdog Бужарово — только публичный канал, NetBird вынесен из alert level
|
||||
|
||||
## Контекст
|
||||
|
||||
Олег в Египте, прислал что бот "сыпал ошибками вчера, попросил отключить мониторинг". Разведка:
|
||||
|
||||
1. **Все сервисы на LXC 139 живы** (`openclaw-gateway`, `buzharovo-watchdog.timer`, `netbird-watchdog.timer`, `netbird.service` — `active+enabled`). Олег ничего не отключал.
|
||||
2. **Watchdog v1 правильно держал `WARNING_NETBIRD`** (последний алерт 13 мая ~19:32 МСК), антиспам корректный — повторных алертов не слал.
|
||||
3. **Истинный источник "ошибок"** — `openclaw primary model = omniroute/cc/claude-opus-4-7` упёрся в лимит Max-подписки:
|
||||
```
|
||||
omniroute (cc/claude-opus-4-7) returned a billing error — your API key has run out of credits
|
||||
400 [400]: You're out of extra usage. Add more at claude.ai/settings/usage and keep going.
|
||||
```
|
||||
Каждое сообщение в боте + каждое ночное `memory-core dreaming` (cron 03:00) → billing 400 → failover на `kr/claude-sonnet-4.5`. На клиенте часть запросов могла отдаться с ошибкой раньше чем failover отработал.
|
||||
4. **Server1C NetBird daemon (Windows)** регулярно flap'ает, `last_seen=2026-05-13T08:24:26` — > 25 часов вне mesh, хотя сервер сам публично жив (ping + RDP 3389 OK).
|
||||
|
||||
## Решения
|
||||
|
||||
### Фикс 1: primary model → free Sonnet 4.5
|
||||
|
||||
`/root/.openclaw/openclaw.json`:
|
||||
```json
|
||||
{
|
||||
"primary": "omniroute/kr/claude-sonnet-4.5",
|
||||
"fallbacks": [
|
||||
"omniroute/cc/claude-sonnet-4-6",
|
||||
"omniroute/gh/claude-sonnet-4.5",
|
||||
"omniroute/cc/claude-opus-4-7"
|
||||
]
|
||||
}
|
||||
```
|
||||
Hot-reload подхватился. Backup конфига — `/root/.openclaw/openclaw.json.bak.opus-billing-<ts>`.
|
||||
|
||||
**Возврат на Opus как primary — только после пополнения Max** или подключения второго конектора в OmniRoute.
|
||||
|
||||
### Фикс 2: watchdog v2 — только публичный канал
|
||||
|
||||
Переписан `/usr/local/bin/buzharovo-watchdog.sh`. Логика alert-level **больше не учитывает NetBird-проверки**:
|
||||
|
||||
- `OK` — `ping 185.13.47.2` ✓ + `TCP 3389` (RDP) ✓
|
||||
- `DEGRADED` — один из публичных упал
|
||||
- `CRITICAL` — оба публичных упали
|
||||
|
||||
NetBird-уровень (`ping 100.70.75.103` + `TCP 5985`) **только логируется** в `state.json` (`ping_nb`, `winrm_nb`), но не меняет level и не порождает алерт.
|
||||
|
||||
При первом алерте в новую сессию (prev_level=INIT) добавляется пометка:
|
||||
> _NetBird до сервера сейчас лежит — это известная регулярная проблема со стороны Windows-сервера, не влияет на работу 1С для пользователей. Watchdog проверяет только публичный канал._
|
||||
|
||||
**Почему так:** NetBird daemon на Server1C (Windows 2012 R2) теряет mesh-сессию регулярно (memory `feedback_netbird_watchdog`). Лечится `Restart-Service netbird` через RDP — но это ручная операция, и поток алертов из-за этого был шумом, а не сигналом. Сервер для пользователей в Бужарово при этом работает — 1С локально доступна.
|
||||
|
||||
**Что теряем:** WinRM-actions (`check_1c_service.py`, `check_rmngr_cpu.py`, `restart_1c_agent.py`, `sql_native_backup.py`) идут через NetBird (`100.70.75.103:5985`). Когда NetBird падает — эти actions недоступны. Бот в группе об этом честно скажет: "Не могу проверить состояние службы 1С — туннель до сервера временно лежит". Восстанавливается после `Restart-Service netbird` на server1c через RDP.
|
||||
|
||||
**Что НЕ теряем:** алерты о реально критичных событиях (сервер физически лёг публично, сеть провайдера упала, RDP закрылся).
|
||||
|
||||
## Деплой
|
||||
|
||||
```bash
|
||||
pct push 139 wd.sh /usr/local/bin/buzharovo-watchdog.sh
|
||||
chmod +x /usr/local/bin/buzharovo-watchdog.sh
|
||||
chown root:root /usr/local/bin/buzharovo-watchdog.sh
|
||||
echo "{}" > /var/lib/severny-les/state.json # force re-evaluate
|
||||
# Manual run → level=OK, alert "Мониторинг включён" ушёл в группу
|
||||
```
|
||||
|
||||
## Обновления в vault и persona
|
||||
|
||||
- `/root/clawd/MEMORY.md` на LXC 139 — добавлены уроки про Opus billing + watchdog v2
|
||||
- [[projects/buzharovo/severny-les-bot]] — обновить ссылку на watchdog v2 (TODO)
|
||||
- Этот decision-файл
|
||||
|
||||
## NetBird route 10.0.0.0/24 — попутно
|
||||
|
||||
Существующий route `Dom` (`cud7q73l0ubs73dr3gc0`) advertised через peer `pve 100.70.121.235 (Эстония)`, **disconnected**. Переключил routing peer на **`openclaw` (`d79s9g2fadhs739mihkg`)** через PUT `/api/routes/cud7q73l0ubs73dr3gc0`. Mac получил доступ к 10.0.0.0/24 через NetBird → openclaw → LAN.
|
||||
|
||||
## TODO (опционально, не сейчас)
|
||||
|
||||
- **Reverse SSH-туннель** server1c → severny-les для WinRM-actions без зависимости от NetBird. Server1C сам делает outbound SSH → LXC 139 пробрасывает 127.0.0.1:55985 → server1c:5985. WinRM-скрипты целятся в `localhost:55985`. Решает проблему "NetBird лёг — WinRM недоступен".
|
||||
- **HTTPS WinRM (5986)** публично с certificate-pin'ом — альтернатива через интернет, но требует настройки SSL и хорошего firewall.
|
||||
- **Ночной cron sql_native_backup.py** — автоматизация ежедневных бэкапов БД (TODO от 2026-05-08, см. соответствующий decision).
|
||||
66
projects/_index.md
Normal file
66
projects/_index.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
date: 2026-05-07
|
||||
type: index
|
||||
source: scripts/kb-objects-map.py
|
||||
tags: [index, registry, objects, netbird]
|
||||
---
|
||||
|
||||
# Реестр объектов и netbird-пиров
|
||||
|
||||
Авто-сгенерировано `2026-05-07T07:10` из [[dttb/netbird-inventory]] + frontmatter в `projects/`.
|
||||
**Не править вручную** — перепишется. Источник правды — frontmatter в каждом README.
|
||||
|
||||
- Проектов: **21**, из них с netbird-привязкой: **9**
|
||||
- NetBird-пиров без projects-страницы: **14** (TODO — создать стабы)
|
||||
|
||||
## Проекты с netbird-привязкой
|
||||
|
||||
| ID | Имена | NetBird IP | OS | Город | Файл | Статус |
|
||||
|---|---|---|---|---|---|---|
|
||||
| benilux | Benilux, OpenWrt Benilux, benilux | 100.70.207.97 | OpenWrt 24.10.3 | Istra | [[projects/benilux/README]] | stub |
|
||||
| dttb | Cups-Server, HomeLab, MacBook-Pro, OpenWrt 1, Work Server dttb | 100.70.242.212, 100.70.239.211, 100.70.200.150, 100.70.152.70, 100.70.92.138, 100.70.0.15, 100.70.121.235, 100.70.191.161, 100.70.219.93, 100.70.57.167, 100.70.12.3, 100.70.211.159, 100.70.121.49, 100.70.128.10, 100.70.100.82 | Darwin 26.3.1, Debian GNU/Linux 12, Debian GNU/Linux 13, OpenWrt 24.10.3, Ubuntu 22.04, Ubuntu 24.04, Windows Server 2025, iOS 26.3.1, iPadOS 18.6.2 | Helsinki, Istra, Moscow | [[projects/dttb/README]] | active |
|
||||
| lipki | Lipki, OpenWrt_Lipki, PSP_Network, lipki, Антон | 100.70.35.234 | OpenWrt 24.10.3 | Istra | [[projects/lipki/README]] | active |
|
||||
| mmfb | DESKTOP-UFULDJQ, LionART, Yuri Vitalievich, mmfb, pve LionART | 100.70.128.49 | Debian GNU/Linux 12 | Istra | [[projects/mmfb/README]] | active |
|
||||
| niikn | Cloud-NIIKN New niikn.com, DESKTOP-IC5A0K2 M.Maul, Kripto-ARM, M.Maul, Maxim Maul | 100.70.117.21, 100.70.145.223, 100.70.120.229, 100.70.178.190 | Debian GNU/Linux 12, Ubuntu 24.04, Windows 10, Windows 11 | Istra | [[projects/niikn/README]] | active |
|
||||
| openwrt-4 | OpenWrt_4, openwrt-4, openwrt4 | 100.70.235.2 | OpenWrt 24.10.3 | Moscow | [[projects/openwrt-4/README]] | stub |
|
||||
| sergey | OpenWrt_Sergey, Sergey, sergey, Одинцово | 100.70.110.164 | OpenWrt 24.10.3 | Odintsovo | [[projects/sergey/README]] | stub |
|
||||
| vishnevyy-sad | OpenWrt Вишневый сад ( Константин ), vishnevyy-sad, Вишневый сад, Вишнёвый сад, Константин | 100.70.152.137 | OpenWrt 24.10.3 | Moscow | [[projects/vishnevyy-sad/README]] | stub |
|
||||
| znamenskoye | 89-111-140-86.swtest.ru, OpenWrt_Znamenskoe_Home, OpenWrt_ohothozyistvo, VPS 89.111.140.86, Znamenskoe | 100.70.93.36, 100.70.54.204, 100.70.63.67, 100.70.137.181, 100.70.100.155 | Debian GNU/Linux 11, OpenWrt 21.02.1, OpenWrt 24.10.3, Ubuntu 24.04 | Helsinki, Istra, Moscow | [[projects/znamenskoye/README]] | active |
|
||||
|
||||
## Проекты без netbird-привязки
|
||||
|
||||
| ID | Тип | Файл | Статус |
|
||||
|---|---|---|---|
|
||||
| all-projects-summary | project-note | [[projects/all-projects-summary]] | unknown |
|
||||
| bitrix-sites | project-note | [[projects/bitrix-sites]] | unknown |
|
||||
| buzharovo | project | [[projects/buzharovo/README]] | active |
|
||||
| clawdbot-bots | project-note | [[projects/clawdbot-bots]] | unknown |
|
||||
| glavtorg | project | [[projects/glavtorg/README]] | active |
|
||||
| homelab-proxmox | project-note | [[projects/homelab-proxmox]] | unknown |
|
||||
| infrastructure-overview | project-note | [[projects/infrastructure-overview]] | unknown |
|
||||
| krasnogorsk | project | [[projects/krasnogorsk/README]] | active |
|
||||
| nextcloud | project-note | [[projects/nextcloud]] | unknown |
|
||||
| unresolved-issues | project-note | [[projects/unresolved-issues]] | unknown |
|
||||
| video-surveillance | project-note | [[projects/video-surveillance]] | unknown |
|
||||
| zelenograd | project | [[projects/zelenograd/README]] | active |
|
||||
|
||||
## NetBird-пиры без projects-страницы — TODO
|
||||
|
||||
Эти пиры есть в инвентаре, но у них нет своей карточки в `projects/`. Бот не сможет ответить на «найди мне X» — нет файла. Нужно создать стабы (Фаза 4 плана).
|
||||
|
||||
| Имя в NetBird | IP | OS | Город | Версия |
|
||||
|---|---|---|---|---|
|
||||
| `DESKTOP-2IOQS54` | 100.70.82.83 | Windows 10 | Saransk | 0.50.3 |
|
||||
| `DESKTOP-5RGAUUG` | 100.70.221.26 | Windows 11 | Istra | |
|
||||
| `DESKTOP-9VJ949T скоморохова` | 100.70.44.183 | Windows 10 | St Petersburg | |
|
||||
| `DESKTOP-AGBMLPN` | 100.70.0.106 | Windows 11 | Helsinki | 0.66.2 |
|
||||
| `DESKTOP-HL0BB05` | 100.70.235.80 | Windows 11 | Lipetsk | 0.59.7 |
|
||||
| `DESKTOP-LBD73OR` | 100.70.78.170 | Windows 11 | Astana | |
|
||||
| `KOMPUTER` | 100.70.83.120 | Windows 11 | St Petersburg | |
|
||||
| `Kolyadenko` | 100.70.146.58 | Windows 10 | | |
|
||||
| `LAPTOP-3IR5EA9J` | 100.70.149.179 | Windows 11 | Mérida | |
|
||||
| `MacBook-Pro-Vera.local` | 100.70.252.228 | Darwin 26.3.1 | St Petersburg | |
|
||||
| `MastaNotebook` | 100.70.116.166 | Windows 11 | Moscow | |
|
||||
| `WIN-BC0OTBOBBCH` | 100.70.181.152 | Windows Server 2025 | Moscow | |
|
||||
| `iPhone-netbird` | 100.70.18.13 | iOS 26.3.1 | | |
|
||||
| `Денис Тихая` | 100.70.155.107 | Windows 10 | | |
|
||||
35
projects/benilux/README.md
Normal file
35
projects/benilux/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: stub
|
||||
tags: [object, openwrt, netbird, client, todo, istra]
|
||||
aliases: [Benilux, benilux, "OpenWrt Benilux"]
|
||||
---
|
||||
|
||||
# Benilux — клиентский OpenWrt в Истре
|
||||
|
||||
**Стаб-заметка**, фактов мало. Создан, чтобы Максимка (FTS) не терял этот объект.
|
||||
|
||||
## Что известно (из NetBird)
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Имя пира | `OpenWrt Benilux` |
|
||||
| NetBird IP | `100.70.207.97` |
|
||||
| Локация (NetBird) | Istra |
|
||||
| OpenWrt | 24.10.3 |
|
||||
| NetBird agent | 0.59.13 |
|
||||
| Группы | `All`, `OpenWRT VPN` |
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [ ] Что такое «Benilux» — название объекта / магазина / клиента?
|
||||
- [ ] Контактное лицо
|
||||
- [ ] Точный адрес в Истре
|
||||
- [ ] Что роутер обслуживает
|
||||
- [ ] LAN, провайдер, WAN
|
||||
- [ ] Пароль/ключ доступа
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`Benilux`, `OpenWrt Benilux`, `100.70.207.97`, `Истра-Benilux`.
|
||||
27
projects/buzharovo/README.md
Normal file
27
projects/buzharovo/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
type: project
|
||||
status: active
|
||||
tags: [buzharovo, client, 1c, windows-server]
|
||||
aliases: [Бужарово, buzharovo, Server1C]
|
||||
---
|
||||
|
||||
# Бужарово
|
||||
|
||||
Боевой сервер 1С организации в селе Бужарово.
|
||||
|
||||
## Хосты
|
||||
|
||||
- **Server1C** — Windows Server 2012 R2, 1С:Предприятие 8.3.27.1606 + MSSQL.
|
||||
- Публичный IP: 185.13.47.2 (RDP:3389)
|
||||
- Netbird: 100.70.75.103 (server1c.netbird.cloud)
|
||||
- Подробности и runbook: [[projects/buzharovo/server1c]]
|
||||
|
||||
- **Северный лес — AI-ассистент** — LXC 139 на dttb-Proxmox, openclaw + watchdog для server1c.
|
||||
- LAN: 10.0.0.240, NetBird: 100.70.212.78
|
||||
- Telegram: `@bz_sl_bot` ("ИИ Ассистент Бужарово ( Северный лес )")
|
||||
- Справочник и runbook: [[projects/buzharovo/severny-les-bot]]
|
||||
- Решение о создании: [[decisions/2026-05-08-severny-les-bot-buzharovo]]
|
||||
|
||||
## Известные рецепты
|
||||
|
||||
- **rmngr-loop после crash** (07.05.2026): тормоза локальных пользователей → `Restart-Service '1C:Enterprise 8.3 Server Agent (x86-64)' -Force`. Полный ребут сервера НЕ помогает. Полный разбор: [[decisions/2026-05-07-buzharovo-1c-rmngr-loop-after-crash]].
|
||||
91
projects/buzharovo/server1c.md
Normal file
91
projects/buzharovo/server1c.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
date: 2026-04-17
|
||||
type: project
|
||||
tags: [dttb]
|
||||
---
|
||||
|
||||
# Server1C — Сервер 1С в Бужарово
|
||||
|
||||
## Подключение
|
||||
- **Публичный IP:** 185.13.47.2 (RDP:3389)
|
||||
- **Netbird IP:** 100.70.75.103 (server1c.netbird.cloud)
|
||||
- **WinRM:** порт 5985, basic auth
|
||||
- **Учётка:** dttb / 1qaz!QAZ
|
||||
- **OS:** Windows Server 2012 R2 (6.3.9600)
|
||||
- **Hostname:** Server1C
|
||||
- **Локация:** Бужарово
|
||||
|
||||
## 1С:Предприятие
|
||||
Три службы агента:
|
||||
1. `1C:Enterprise 8.3 Server Agent` — StartType: Automatic
|
||||
2. `1C:Enterprise 8.3 Server Agent (x86-64)` — StartType: Automatic
|
||||
3. `RagentServer_8327` — версия 8.3.27.1606, StartType: Automatic
|
||||
|
||||
### Решено: конфликт служб при загрузке (2026-04-16)
|
||||
**Проблема:** 3 службы с Automatic стартовали одновременно, боролись за порты 1540/1541.
|
||||
- Служба 8.3.18 (x86) — бинарник удалён, падала с ошибкой "файл не найден"
|
||||
- RagentServer_8327 — дубликат без параметров, таймаут на портах
|
||||
- Рабочая: `1C:Enterprise 8.3 Server Agent (x86-64)` (8.3.27.1606)
|
||||
|
||||
**Решение:** отключены лишние службы (Disabled), оставлена только x86-64.
|
||||
|
||||
### Решено: rmngr-loop после грязного ребута (2026-05-07)
|
||||
**Симптомы:** утром локальные пользователи в Бужарово жалуются на резко тормозящую 1С. Удалённое подключение к 1С через NetBird ни при чём — проблема видна и в локальной сети.
|
||||
|
||||
**Диагноз:**
|
||||
- Сервер ушёл в crash в 09:05 (Event 41 Kernel-Power: rebooted without cleanly shutting down + EventLog 6008: previous shutdown was unexpected).
|
||||
- После загрузки `rmngr.exe` (менеджер кластера 1С) держит ~1.7 ядра постоянно вместо штатных <2% в idle. `rphost`, `sqlservr`, диски, сеть — в норме.
|
||||
- `rac.exe localhost:1540 cluster list` отваливается с "ошибка соединения с сервером" — admin-канал rmngr повис, кластер не отвечает на управление.
|
||||
- `netbird.exe` параллельно крутит 1.4 ядра — это reconnect-loop как побочка crash, после ребута сам приходит в норму.
|
||||
|
||||
**Рецепт:**
|
||||
1. Замерить дельту CPU через `Get-Process | %{$_.CPU}` за 5 секунд — если у `rmngr` >50% одного ядра в idle, диагноз подтверждён.
|
||||
2. **Полный ребут сервера НЕ помогает** — после загрузки rmngr опять начинает жрать CPU. На свежезагруженном сервере uptime=0.6 min: rmngr уже на 178% ядра.
|
||||
3. **Помогает рестарт службы:** `Restart-Service -Name '1C:Enterprise 8.3 Server Agent (x86-64)' -Force`. После рестарта rmngr возвращается к 3% ядра. Все активные сеансы пользователей вылетят — нужно их предупредить.
|
||||
4. После рестарта может остаться орфан-процесс `ragent` от прошлого старта (не слушает 1540, висит). Прибить вручную: `Stop-Process -Id <pid> -Force`.
|
||||
|
||||
**Why:** причина рута rmngr-loop неясна — возможно повреждение кэша `srvinfo`, регресс 8.3.27.1606, или Disabled-служба `RagentServer_8327` мешает первому запуску ragent. Если повторится — смотреть `C:\Program Files\1cv8\srvinfo\reg_*\1Cv8FTLog\` на ошибки.
|
||||
|
||||
**Долгосрочно:** настроить несколько `rphost` в кластере (по одному на 8-12 сеансов) — сейчас один rphost на всех локальных юзеров = бутылочное горлышко.
|
||||
|
||||
## MS SQL Server (для server1c\RitmUl)
|
||||
|
||||
- **Instance:** `localhost` (default, MSSQL11 = SQL Server 2012 SP4)
|
||||
- **SA / Qwer1122334400** (полные права на все БД)
|
||||
- **БД:** `RitmUl` (~3.8 GB), также есть `Accounting`, `Retail_2021`, `Retail_2021demo`
|
||||
- **Connection string:** `Server=localhost;Database=master;User Id=sa;Password=Qwer1122334400;`
|
||||
|
||||
## Бэкапы — native SQL, не Effector Saver
|
||||
|
||||
**Канон от 2026-05-08:** `BACKUP DATABASE` через SQL Server, не DT-выгрузка через Effector Saver. Подробности и причины — в [[decisions/2026-05-08-buzharovo-sql-native-backup]].
|
||||
|
||||
Команда:
|
||||
```sql
|
||||
BACKUP DATABASE [RitmUl]
|
||||
TO DISK = N'C:\backup\RitmUl_<timestamp>.bak'
|
||||
WITH FORMAT, INIT, COMPRESSION, COPY_ONLY, STATS = 5
|
||||
```
|
||||
|
||||
- **Папка:** `C:\backup\` (на C: было 374 GB свободно на 2026-05-08)
|
||||
- **Время:** ~2 сек на 3.8 GB БД
|
||||
- **Размер:** ~30% от оригинала (3.8 GB → 1.1 GB сжатый)
|
||||
- **Online:** не требует отключения пользователей, не требует cluster admin'а 1С
|
||||
- **Скрипт:** `/root/clawd/scripts/sql_native_backup.py` на LXC 139 (severny-les bot)
|
||||
|
||||
## Кластер 1С — известные проблемы
|
||||
|
||||
### Cluster admin отсутствует, и его нельзя добавить
|
||||
Серверная консоль 1С → `Локальный кластер → Администраторы` показывает 0, но при попытке создать через GUI требует логин cluster admin'а (которого нет) — chicken-and-egg. Через `rac` и `V83.COMConnector` — то же самое. Agent admin (создан 2026-05-08, `admin/1qaz!QAZ` на уровне `(*)Server1C → Администраторы`) **не поднимает права на cluster operations**.
|
||||
|
||||
**Последствия:**
|
||||
- Effector Saver задача `Бэкап 1Cv8` падает с `Администратор кластера не аутентифицирован (HRESULT=80004005)` → не может вызвать `TerminateSession` → не может получить эксклюзив на ИБ.
|
||||
- Все cluster operations (просмотр сессий, kill сессий, блокировка соединений) недоступны через API.
|
||||
|
||||
**Что НЕ помогло:** SQL `KILL` сессий через sa — 1С rphost восстанавливает соединения за 1-2 мин, и persistent session_id (например `КулябинПИ 4514` от 12:55:42 в день 2026-05-08) reanimate.
|
||||
|
||||
**Workaround:** SQL native backup (см. выше) — обходит всю эту историю с эксклюзивом.
|
||||
|
||||
**Как лечить (не сделано, рискованно):** обнулить `C:\Program Files\1cv8\srvinfo\reg_1541\1CV8Clst.lst` → потеряются и админы и регистрация ИБ → перерегистрировать ИБ через SQL params (`SA/Qwer1122334400`, host `localhost`, db `RitmUl`).
|
||||
|
||||
### V83.COMConnector x64 зарегистрирован
|
||||
2026-05-08 я через `regsvr32` зарегистрировал `C:\Program Files\1cv8\8.3.27.1606\bin\comcntr.dll` в `HKLM\SOFTWARE\Classes\V83.COMConnector` (только x64; x86 платформа на сервере не установлена). В Effector Saver вручную переключено на "64-разрядный V83.ComConnector" → `HRESULT=800401F3` ушёл.
|
||||
173
projects/buzharovo/severny-les-bot.md
Normal file
173
projects/buzharovo/severny-les-bot.md
Normal file
@@ -0,0 +1,173 @@
|
||||
---
|
||||
date: 2026-05-08
|
||||
type: project
|
||||
tags: [buzharovo, bot, openclaw, watchdog, monitoring]
|
||||
---
|
||||
|
||||
# Северный лес — AI-ассистент для server1c
|
||||
|
||||
> Создан 2026-05-08, до отпуска Олега в Египте (2026-05-09 → 2026-05-22). Цель — пока Олега нет, кто-то на стороне Бужарово видит в Telegram-группе что происходит с сервером и может ткнуть `/approve` на восстановительные действия.
|
||||
|
||||
## Что это
|
||||
|
||||
Отдельный AI-бот на стеке **openclaw 2026.5.7**, заточен только под мониторинг и реагирование на инциденты сервера 1С в Бужарово. Watchdog-слой работает независимо от openclaw и шлёт алерты в Telegram напрямую через bot API — даже если AI-часть упала, уведомления всё равно дойдут.
|
||||
|
||||
**Не путать с:**
|
||||
- LXC 137 [[projects/dttb/openclaw|Максимка]] — основной AI-бот Олега, обслуживает всю инфраструктуру.
|
||||
- LXC 114 [[projects/niikn/clawdbot-niikn|Максимка-Мауля]] — бот в НИИКН для Максима Мауля.
|
||||
|
||||
## Расположение
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Proxmox LXC | **139** (hostname `severny-les`) |
|
||||
| IP LAN | `10.0.0.240` |
|
||||
| NetBird IP | `100.70.212.78` (FQDN `severny-les.netbird.cloud`) |
|
||||
| Ресурсы | 2 cores / 4 GB RAM / 10 GB disk (Ubuntu 24.04) |
|
||||
| Доступ | `sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 139 -- bash"` |
|
||||
|
||||
## Telegram
|
||||
|
||||
- **Bot username:** `@bz_sl_bot`
|
||||
- **Display name:** "ИИ Ассистент Бужарово ( Северный лес )"
|
||||
- **Bot ID:** `8322860033`
|
||||
- **Token:** см. `/root/.openclaw/openclaw.json` → `channels.telegram.botToken` (или `/etc/severny-les/watchdog.env`)
|
||||
- **Allowlist:** Олег `1292155421` (DM). Группа — пока пустая, обновим `groupAllowFrom` когда бот будет добавлен в TG-группу руководящего состава.
|
||||
|
||||
## Стек и сервисы
|
||||
|
||||
### openclaw 2026.5.7 (system-level systemd)
|
||||
- **Конфиг:** `/root/.openclaw/openclaw.json`
|
||||
- **Workspace:** `/root/clawd/` (IDENTITY/INFRASTRUCTURE/USER/SOUL/MEMORY/TOOLS/HEARTBEAT.md + `scripts/`)
|
||||
- **Unit:** `/etc/systemd/system/openclaw-gateway.service` (НЕ `--user` как на 137 — в LXC без sessions это не работает)
|
||||
- **Gateway port:** `18790` (на 137 — 18789, чтобы не путать)
|
||||
- **Primary model:** `omniroute/cc/claude-opus-4-7` (Opus 4.7 через Max), fallbacks: `kr/claude-sonnet-4.5` → `cc/claude-sonnet-4-6`
|
||||
- **Plugin bonjour:** disabled (превентивно от mDNS-крэшей в LXC)
|
||||
- **NODE_OPTIONS:** `--dns-result-order=ipv4first` (превентивно от Telegram IPv6-сбоев)
|
||||
|
||||
```bash
|
||||
systemctl status openclaw-gateway.service
|
||||
systemctl restart openclaw-gateway.service
|
||||
journalctl -u openclaw-gateway.service -n 50 --no-pager
|
||||
```
|
||||
|
||||
### buzharovo-watchdog (system-level systemd timer, каждые 60s)
|
||||
- **Скрипт:** `/usr/local/bin/buzharovo-watchdog.sh`
|
||||
- **Unit:** `/etc/systemd/system/buzharovo-watchdog.{service,timer}`
|
||||
- **Env:** `/etc/severny-les/watchdog.env` (TG token + chat_id)
|
||||
- **State:** `/var/lib/severny-les/state.json` — антиспам (алерт только при смене уровня)
|
||||
|
||||
Уровни: `OK` / `WARNING` (часть проверок упала) / `WARNING_NETBIRD` (NB до server1c лежит, публично OK) / `CRITICAL` (сервер недоступен и публично, и через NetBird).
|
||||
|
||||
Алерт уходит в TG **напрямую** через `https://api.telegram.org/bot.../sendMessage`, мимо openclaw. Если openclaw упал — алерт всё равно придёт.
|
||||
|
||||
```bash
|
||||
journalctl -t buzharovo-watchdog --since "1 hour ago" -n 30
|
||||
systemctl list-timers buzharovo-watchdog.timer
|
||||
# Тестовый прогон:
|
||||
set -a; . /etc/severny-les/watchdog.env; set +a; /usr/local/bin/buzharovo-watchdog.sh
|
||||
```
|
||||
|
||||
### netbird-watchdog (как на LXC 132/137)
|
||||
- **Скрипт:** `/usr/local/bin/netbird-watchdog.sh` (порт с LXC 137)
|
||||
- **Unit:** `/etc/systemd/system/netbird-watchdog.{service,timer}` (каждые 2 мин)
|
||||
- При `Relays: 0/N` или `Peers: 0/N` (когда N>0) и Management=Connected — `systemctl restart netbird` (минимум 5 мин между рестартами).
|
||||
|
||||
### Heartbeat (cron */5)
|
||||
- `/etc/cron.d/severny-les-heartbeat` → `/root/clawd/scripts/heartbeat.sh` пишет timestamp в `/tmp/severny-les-heartbeat`
|
||||
- buzharovo-watchdog проверяет: если heartbeat старше 600s — добавляет в алерт строчку про "openclaw молчит" (анти-спам: не чаще 1 раз в час).
|
||||
|
||||
## Что бот может делать (TOOLS.md)
|
||||
|
||||
### Без `/approve` (read-only)
|
||||
- `/status` — общий статус (ping/порты публично + через NetBird, состояние 1С службы, CPU rmngr)
|
||||
- `/check_1c` — три 1С службы через WinRM (Running/Stopped/Disabled)
|
||||
- `/check_rmngr` — детектор rmngr-loop (CPU rmngr.exe за 5 секунд, >50% = диагноз)
|
||||
|
||||
### С `/approve` от Олега `1292155421`
|
||||
- `/restart_1c` → `Restart-Service '1C:Enterprise 8.3 Server Agent (x86-64)' -Force`. Все сеансы 1С вылетят. Это рецепт от 2026-05-07 rmngr-loop ([[decisions/2026-05-07-buzharovo-1c-rmngr-loop-after-crash]]).
|
||||
- `/kill_orphan_ragent` — найти `ragent.exe` без LISTENING на 1540 и `Stop-Process -Force` (зомби после restart_1c).
|
||||
|
||||
Ребут сервера НЕ даём — по опыту 2026-05-07 ребут rmngr-loop не помогает, простой добавляет.
|
||||
|
||||
`exec-approvals.json` лежит в `/root/.openclaw/exec-approvals.json` — это whitelist для openclaw. Команды не из whitelist openclaw отказывается выполнять.
|
||||
|
||||
## WinRM на server1c
|
||||
|
||||
- **Адрес:** `100.70.75.103:5985` (через NetBird, basic, http) — публично 5985 закрыт
|
||||
- **Учётка:** `dttb` / `1qaz!QAZ`
|
||||
- **Python:** `pywinrm` уже стоит. Обёртка — `/root/clawd/scripts/winrm_lib.py`.
|
||||
|
||||
⚠️ **На 2026-05-08 NetBird ACL до server1c пока НЕ работает** — handshake не идёт (`Required key not available`). Олегу нужно в NetBird Dashboard разрешить `severny-les` доступ к `server1c` хотя бы на порты 5985 (WinRM) + 1 ICMP + 3389 (опц.). До этого WinRM-actions не работают, watchdog мониторит только публичные проверки.
|
||||
|
||||
## Скрипты в `/root/clawd/scripts/`
|
||||
|
||||
| Скрипт | Что делает |
|
||||
|---|---|
|
||||
| `check_buzharovo.sh` | bash, общий статус (ping+порты), без WinRM |
|
||||
| `winrm_lib.py` | обёртка pywinrm, переиспользуют все py-скрипты |
|
||||
| `check_1c_service.py` | 3 службы 1С через WinRM |
|
||||
| `check_rmngr_cpu.py` | детектор rmngr-loop |
|
||||
| `restart_1c_agent.py` | `Restart-Service '1C:...'` (требует /approve) |
|
||||
| `kill_orphan_ragent.py` | убить зомби ragent (требует /approve) |
|
||||
| `heartbeat.sh` | cron, пишет timestamp |
|
||||
| `sql_native_backup.py` | **бэкап ИБ через MS SQL Server `BACKUP DATABASE` с компрессией** (см. [[decisions/2026-05-08-buzharovo-sql-native-backup]]). Online, ~2 сек на 3.8 GB, не требует cluster admin 1С |
|
||||
|
||||
## Сценарии работы
|
||||
|
||||
### Олег в отпуске, ночью упал rmngr
|
||||
1. Watchdog на 60-й секунде заметил: WinRM `1C:Enterprise 8.3 Server Agent (x86-64)` всё ещё Running, **но** `check_rmngr_cpu.py` вернул `RMNGR_LOOP`.
|
||||
2. Watchdog шлёт в TG-группу: 🚨 Северный лес — rmngr-loop на server1c. CPU rmngr 67%. Предлагаю `/restart_1c`.
|
||||
3. Дежурный из руководящего состава отвечает в группу, бот ему: "Ок, но нужно `/approve` от Олега. Можете позвонить ему? Или подождать утра — время до критичного простоя ~2 часа."
|
||||
4. Олег с пляжа делает `/approve restart_1c` → бот выполняет → отписывается в группу что прошло.
|
||||
|
||||
### Сервер недоступен публично
|
||||
1. Watchdog: 3 подряд провала ping `185.13.47.2` за 3 минуты + RDP не отвечает.
|
||||
2. Шлёт в группу: 🚨 server1c НЕДОСТУПЕН. Не отвечает ни публично, ни через NetBird. Похоже сервер лёг или сеть провайдера.
|
||||
3. Бот ничего не может сделать сам — это VDS у внешнего провайдера. Эскалирует на Олега, ждёт ручного вмешательства.
|
||||
|
||||
### NetBird до server1c упал, публично всё OK
|
||||
1. Watchdog: ping `185.13.47.2` ОК, ping `100.70.75.103` нет.
|
||||
2. Шлёт: ⚠️ NetBird до server1c лежит. Публично сервер виден. WinRM-actions недоступны.
|
||||
3. Сервер сам по себе работает — пользователи в Бужарово 1С видят. Но бот не может делать диагностику/рестарты пока NetBird не починен.
|
||||
|
||||
## Чек-лист после возвращения Олега из Египта
|
||||
|
||||
- [ ] Прописать в NetBird Dashboard ACL `severny-les` → `server1c` (5985 TCP минимум).
|
||||
- [ ] Добавить @bz_sl_bot в TG-группу руководящего состава Северного леса; узнать `chat_id` группы.
|
||||
- [ ] Обновить `/etc/severny-les/watchdog.env` BZ_TG_CHAT на групповой chat_id.
|
||||
- [ ] Обновить `/root/.openclaw/openclaw.json` `channels.telegram.groupAllowFrom` — добавить chat_id группы.
|
||||
- [ ] Сделать smoke-test `/restart_1c` (на тестовых выходных, не в боевые часы) — убедиться что openclaw реально дёргает скрипт после `/approve`.
|
||||
- [ ] После миграции server1c на свой сервер — обновить IP в `/root/clawd/INFRASTRUCTURE.md` и в watchdog-скрипте.
|
||||
|
||||
## Известные ограничения и риски
|
||||
|
||||
- **NetBird ACL** на момент создания не пускает severny-les к server1c. Watchdog мониторит публично + через NetBird пингом; WinRM-команды (диагностика 1С, рестарт службы) не работают пока ACL не настроен.
|
||||
- **Группа TG ещё не настроена** — алерты идут в личку Олегу. Когда @bz_sl_bot добавят в группу — поправить env+config.
|
||||
- **Bonjour отключен** превентивно (был crash-loop на 137, см. [[projects/dttb/openclaw#Проблема-Crash-loop-каждые-40-сек]]).
|
||||
- **DNS LXC 139** идёт на `1.1.1.1`/`8.8.8.8` напрямую (через `pct set --nameserver`), не на `10.0.0.1` — иначе FakeIP от Mihomo ломает Telegram API.
|
||||
- **openclaw на system-level** (не --user как на 137). В LXC без user-session systemd --user недоступен.
|
||||
|
||||
## Бэкап
|
||||
|
||||
```bash
|
||||
# на самом LXC 139
|
||||
tar czf /root/severny-les-state-$(date +%F).tar.gz /root/.openclaw /root/clawd /etc/systemd/system/buzharovo-watchdog.* /etc/systemd/system/netbird-watchdog.* /etc/systemd/system/openclaw-gateway.service /etc/severny-les /etc/cron.d/severny-les-heartbeat /usr/local/bin/buzharovo-watchdog.sh /usr/local/bin/netbird-watchdog.sh
|
||||
|
||||
# скопировать наружу
|
||||
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct pull 139 /root/severny-les-state-*.tar.gz /var/lib/vz/dump/"
|
||||
```
|
||||
|
||||
## Откат
|
||||
|
||||
Если бот сломал что-то и его нужно убрать целиком:
|
||||
|
||||
```bash
|
||||
# stop and disable
|
||||
pct exec 139 -- systemctl disable --now openclaw-gateway.service buzharovo-watchdog.timer netbird-watchdog.timer
|
||||
|
||||
# либо целиком LXC
|
||||
pct stop 139 && pct destroy 139
|
||||
```
|
||||
|
||||
NetBird-пир `severny-les.netbird.cloud` останется в Dashboard — нужно удалить руками.
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: project
|
||||
status: active
|
||||
tags: [dttb, homelab, infrastructure]
|
||||
aliases: [HomeLab, dttb, "dttb.ru", "Work Server dttb", "code-server", "rustdeskserver", "MacBook-Pro", "iPhone-batlaew", "iPad-batlaew", "finland5870.com", "cloud", "kasm", "pdm", "OpenWrt 1", "pve ded_mozay", "Cups-Server", "clawdbot", "clawdbot-1"]
|
||||
---
|
||||
|
||||
# Проект DTTB (HomeLab)
|
||||
|
||||
## Инфраструктура
|
||||
@@ -50,9 +57,7 @@
|
||||
- [[projects/dttb/openclaw]]
|
||||
- [[projects/dttb/openwrt-router]]
|
||||
- [[projects/dttb/proxmox-inventory]]
|
||||
- [[projects/dttb/server1c]]
|
||||
- [[projects/dttb/spaceweb-dns]]
|
||||
- [[projects/dttb/video-surveillance-report]]
|
||||
- [[projects/dttb/videonablyudenie-znam]]
|
||||
- [[projects/dttb/znamenskoye-network-topology]]
|
||||
- [[projects/dttb/nextcloud-talk-bot/README]]
|
||||
|
||||
138
projects/dttb/graphify-out/.graphify_analysis.json
Normal file
138
projects/dttb/graphify-out/.graphify_analysis.json
Normal file
@@ -0,0 +1,138 @@
|
||||
{
|
||||
"communities": {
|
||||
"0": [
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_get_ai_reply",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_main",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_114",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_153",
|
||||
"nextcloud_talk_bot_py"
|
||||
],
|
||||
"1": [
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_join_room",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_130",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_141",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_93",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_send_message"
|
||||
],
|
||||
"2": [
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_load_knowledge_base",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_35",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_71"
|
||||
],
|
||||
"3": [
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id",
|
||||
"nextcloud_talk_bot_nextcloud_talk_bot_rationale_107"
|
||||
]
|
||||
},
|
||||
"cohesion": {
|
||||
"0": 0.47,
|
||||
"1": 0.4,
|
||||
"2": 0.5,
|
||||
"3": 1.0
|
||||
},
|
||||
"gods": [
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"label": "nc_request()",
|
||||
"degree": 6
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_main",
|
||||
"label": "main()",
|
||||
"degree": 6
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_send_message",
|
||||
"label": "send_message()",
|
||||
"degree": 5
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt",
|
||||
"label": "build_system_prompt()",
|
||||
"degree": 4
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id",
|
||||
"label": "get_last_message_id()",
|
||||
"degree": 4
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages",
|
||||
"label": "poll_new_messages()",
|
||||
"degree": 4
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_join_room",
|
||||
"label": "join_room()",
|
||||
"degree": 4
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_load_knowledge_base",
|
||||
"label": "load_knowledge_base()",
|
||||
"degree": 3
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_get_ai_reply",
|
||||
"label": "get_ai_reply()",
|
||||
"degree": 3
|
||||
},
|
||||
{
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_35",
|
||||
"label": "Load all .md files from knowledge-base repo into context string",
|
||||
"degree": 1
|
||||
}
|
||||
],
|
||||
"surprises": [
|
||||
{
|
||||
"source": "build_system_prompt()",
|
||||
"target": "main()",
|
||||
"source_files": [
|
||||
"nextcloud-talk-bot.py",
|
||||
"nextcloud-talk-bot.py"
|
||||
],
|
||||
"confidence": "EXTRACTED",
|
||||
"relation": "calls",
|
||||
"note": "Bridges community 2 \u2192 community 0"
|
||||
},
|
||||
{
|
||||
"source": "nc_request()",
|
||||
"target": "get_last_message_id()",
|
||||
"source_files": [
|
||||
"nextcloud-talk-bot.py",
|
||||
"nextcloud-talk-bot.py"
|
||||
],
|
||||
"confidence": "EXTRACTED",
|
||||
"relation": "calls",
|
||||
"note": "Bridges community 1 \u2192 community 3"
|
||||
},
|
||||
{
|
||||
"source": "nc_request()",
|
||||
"target": "poll_new_messages()",
|
||||
"source_files": [
|
||||
"nextcloud-talk-bot.py",
|
||||
"nextcloud-talk-bot.py"
|
||||
],
|
||||
"confidence": "EXTRACTED",
|
||||
"relation": "calls",
|
||||
"note": "Bridges community 1 \u2192 community 0"
|
||||
},
|
||||
{
|
||||
"source": "get_last_message_id()",
|
||||
"target": "main()",
|
||||
"source_files": [
|
||||
"nextcloud-talk-bot.py",
|
||||
"nextcloud-talk-bot.py"
|
||||
],
|
||||
"confidence": "EXTRACTED",
|
||||
"relation": "calls",
|
||||
"note": "Bridges community 3 \u2192 community 0"
|
||||
}
|
||||
],
|
||||
"tokens": {
|
||||
"input": 0,
|
||||
"output": 0
|
||||
}
|
||||
}
|
||||
1
projects/dttb/graphify-out/.graphify_root
Normal file
1
projects/dttb/graphify-out/.graphify_root
Normal file
@@ -0,0 +1 @@
|
||||
/root/knowledge-base/projects/dttb
|
||||
74
projects/dttb/graphify-out/GRAPH_REPORT.md
Normal file
74
projects/dttb/graphify-out/GRAPH_REPORT.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Graph Report - dttb (2026-05-06)
|
||||
|
||||
## Corpus Check
|
||||
- 1 files · ~22,438 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 18 nodes · 28 edges · 4 communities (3 shown, 1 thin omitted)
|
||||
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
|
||||
- Token cost: 0 input · 0 output
|
||||
|
||||
## Graph Freshness
|
||||
- Built from commit: `3220238c`
|
||||
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||
- Run `graphify update .` after code changes (no API cost).
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
- [[_COMMUNITY_Community 0|Community 0]]
|
||||
- [[_COMMUNITY_Community 1|Community 1]]
|
||||
- [[_COMMUNITY_Community 2|Community 2]]
|
||||
- [[_COMMUNITY_Community 3|Community 3]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `nc_request()` - 6 edges
|
||||
2. `main()` - 6 edges
|
||||
3. `send_message()` - 5 edges
|
||||
4. `build_system_prompt()` - 4 edges
|
||||
5. `get_last_message_id()` - 4 edges
|
||||
6. `poll_new_messages()` - 4 edges
|
||||
7. `join_room()` - 4 edges
|
||||
8. `load_knowledge_base()` - 3 edges
|
||||
9. `get_ai_reply()` - 3 edges
|
||||
10. `Load all .md files from knowledge-base repo into context string` - 1 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `main()` --calls--> `build_system_prompt()` [EXTRACTED]
|
||||
nextcloud-talk-bot.py → nextcloud-talk-bot.py _Bridges community 2 → community 0_
|
||||
- `get_last_message_id()` --calls--> `nc_request()` [EXTRACTED]
|
||||
nextcloud-talk-bot.py → nextcloud-talk-bot.py _Bridges community 1 → community 3_
|
||||
- `poll_new_messages()` --calls--> `nc_request()` [EXTRACTED]
|
||||
nextcloud-talk-bot.py → nextcloud-talk-bot.py _Bridges community 1 → community 0_
|
||||
- `main()` --calls--> `get_last_message_id()` [EXTRACTED]
|
||||
nextcloud-talk-bot.py → nextcloud-talk-bot.py _Bridges community 3 → community 0_
|
||||
|
||||
## Communities (4 total, 1 thin omitted)
|
||||
|
||||
### Community 0 - "Community 0"
|
||||
Cohesion: 0.47
|
||||
Nodes (5): get_ai_reply(), main(), poll_new_messages(), Long-poll for new messages after last_id, Get reply from Claude via cliproxy
|
||||
|
||||
### Community 1 - "Community 1"
|
||||
Cohesion: 0.4
|
||||
Nodes (6): join_room(), nc_request(), Join conversation as bot user, Send message as bot user, Nextcloud OCS API request, send_message()
|
||||
|
||||
### Community 2 - "Community 2"
|
||||
Cohesion: 0.5
|
||||
Nodes (4): build_system_prompt(), load_knowledge_base(), Load all .md files from knowledge-base repo into context string, Build system prompt with knowledge base
|
||||
|
||||
## Knowledge Gaps
|
||||
- **8 isolated node(s):** `Load all .md files from knowledge-base repo into context string`, `Build system prompt with knowledge base`, `Nextcloud OCS API request`, `Get the highest message ID in the conversation`, `Long-poll for new messages after last_id` (+3 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **1 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `nc_request()` connect `Community 1` to `Community 0`, `Community 3`?**
|
||||
_High betweenness centrality (0.176) - this node is a cross-community bridge._
|
||||
- **Why does `main()` connect `Community 0` to `Community 1`, `Community 2`, `Community 3`?**
|
||||
_High betweenness centrality (0.132) - this node is a cross-community bridge._
|
||||
- **Why does `send_message()` connect `Community 1` to `Community 0`?**
|
||||
_High betweenness centrality (0.129) - this node is a cross-community bridge._
|
||||
- **What connects `Load all .md files from knowledge-base repo into context string`, `Build system prompt with knowledge base`, `Nextcloud OCS API request` to the rest of the system?**
|
||||
_8 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
File diff suppressed because one or more lines are too long
305
projects/dttb/graphify-out/graph.html
Normal file
305
projects/dttb/graphify-out/graph.html
Normal file
File diff suppressed because one or more lines are too long
464
projects/dttb/graphify-out/graph.json
Normal file
464
projects/dttb/graphify-out/graph.json
Normal file
@@ -0,0 +1,464 @@
|
||||
{
|
||||
"directed": false,
|
||||
"multigraph": false,
|
||||
"graph": {},
|
||||
"nodes": [
|
||||
{
|
||||
"label": "nextcloud-talk-bot.py",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L1",
|
||||
"community": 0,
|
||||
"norm_label": "nextcloud-talk-bot.py",
|
||||
"id": "nextcloud_talk_bot_py"
|
||||
},
|
||||
{
|
||||
"label": "load_knowledge_base()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L34",
|
||||
"community": 2,
|
||||
"norm_label": "load_knowledge_base()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_load_knowledge_base"
|
||||
},
|
||||
{
|
||||
"label": "build_system_prompt()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L70",
|
||||
"community": 2,
|
||||
"norm_label": "build_system_prompt()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt"
|
||||
},
|
||||
{
|
||||
"label": "nc_request()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L92",
|
||||
"community": 1,
|
||||
"norm_label": "nc_request()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request"
|
||||
},
|
||||
{
|
||||
"label": "get_last_message_id()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L106",
|
||||
"community": 3,
|
||||
"norm_label": "get_last_message_id()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id"
|
||||
},
|
||||
{
|
||||
"label": "poll_new_messages()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L113",
|
||||
"community": 0,
|
||||
"norm_label": "poll_new_messages()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages"
|
||||
},
|
||||
{
|
||||
"label": "join_room()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L129",
|
||||
"community": 1,
|
||||
"norm_label": "join_room()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_join_room"
|
||||
},
|
||||
{
|
||||
"label": "send_message()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L140",
|
||||
"community": 1,
|
||||
"norm_label": "send_message()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_send_message"
|
||||
},
|
||||
{
|
||||
"label": "get_ai_reply()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L152",
|
||||
"community": 0,
|
||||
"norm_label": "get_ai_reply()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_get_ai_reply"
|
||||
},
|
||||
{
|
||||
"label": "main()",
|
||||
"file_type": "code",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L196",
|
||||
"community": 0,
|
||||
"norm_label": "main()",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"label": "Load all .md files from knowledge-base repo into context string",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L35",
|
||||
"community": 2,
|
||||
"norm_label": "load all .md files from knowledge-base repo into context string",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_35"
|
||||
},
|
||||
{
|
||||
"label": "Build system prompt with knowledge base",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L71",
|
||||
"community": 2,
|
||||
"norm_label": "build system prompt with knowledge base",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_71"
|
||||
},
|
||||
{
|
||||
"label": "Nextcloud OCS API request",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L93",
|
||||
"community": 1,
|
||||
"norm_label": "nextcloud ocs api request",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_93"
|
||||
},
|
||||
{
|
||||
"label": "Get the highest message ID in the conversation",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L107",
|
||||
"community": 3,
|
||||
"norm_label": "get the highest message id in the conversation",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_107"
|
||||
},
|
||||
{
|
||||
"label": "Long-poll for new messages after last_id",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L114",
|
||||
"community": 0,
|
||||
"norm_label": "long-poll for new messages after last_id",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_114"
|
||||
},
|
||||
{
|
||||
"label": "Join conversation as bot user",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L130",
|
||||
"community": 1,
|
||||
"norm_label": "join conversation as bot user",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_130"
|
||||
},
|
||||
{
|
||||
"label": "Send message as bot user",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L141",
|
||||
"community": 1,
|
||||
"norm_label": "send message as bot user",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_141"
|
||||
},
|
||||
{
|
||||
"label": "Get reply from Claude via cliproxy",
|
||||
"file_type": "rationale",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L153",
|
||||
"community": 0,
|
||||
"norm_label": "get reply from claude via cliproxy",
|
||||
"id": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_153"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L34",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_load_knowledge_base"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L70",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L92",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L106",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L113",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L129",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_join_room"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L140",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_send_message"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L152",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_get_ai_reply"
|
||||
},
|
||||
{
|
||||
"relation": "contains",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L196",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_py",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L72",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_load_knowledge_base",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L35",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_load_knowledge_base",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_35"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L204",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L71",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_build_system_prompt",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_71"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L108",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L116",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L132",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_join_room"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L146",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_send_message"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L93",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_nc_request",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_93"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L208",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L107",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_get_last_message_id",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_107"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L214",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L114",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_poll_new_messages",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_114"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L142",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_join_room",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_send_message"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L130",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_join_room",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_130"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L242",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_send_message",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L141",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_send_message",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_141"
|
||||
},
|
||||
{
|
||||
"relation": "calls",
|
||||
"context": "call",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L249",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_get_ai_reply",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_main"
|
||||
},
|
||||
{
|
||||
"relation": "rationale_for",
|
||||
"confidence": "EXTRACTED",
|
||||
"source_file": "nextcloud-talk-bot.py",
|
||||
"source_location": "L153",
|
||||
"weight": 1.0,
|
||||
"confidence_score": 1.0,
|
||||
"source": "nextcloud_talk_bot_nextcloud_talk_bot_get_ai_reply",
|
||||
"target": "nextcloud_talk_bot_nextcloud_talk_bot_rationale_153"
|
||||
}
|
||||
],
|
||||
"hyperedges": [],
|
||||
"built_at_commit": "f6bf12ccf957ac62fd694aca23076d9cdedb1581"
|
||||
}
|
||||
134
projects/dttb/graphify-out/manifest.json
Normal file
134
projects/dttb/graphify-out/manifest.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"/root/knowledge-base/projects/dttb/nextcloud-talk-bot/nextcloud-talk-bot.py": {
|
||||
"mtime": 1772553250.5498495,
|
||||
"hash": "37c31f385bf367dffa5410fca5db2aad"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/npm-homelab.md": {
|
||||
"mtime": 1776533040.2587712,
|
||||
"hash": "9037da8a80c3728a1c3ae9e30307d4c1"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/nextcloud.md": {
|
||||
"mtime": 1776533040.2587712,
|
||||
"hash": "0b1fac8b98394b2eeaab825e73725b54"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/vpn-clients.md": {
|
||||
"mtime": 1777491602.2761946,
|
||||
"hash": "6829b65fd532c33c8afc596c01b86c25"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/rustdesk.md": {
|
||||
"mtime": 1777535102.3840268,
|
||||
"hash": "5a1ea382a62ecce3751bbccf82c2e2da"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/proxmox-inventory.md": {
|
||||
"mtime": 1777491602.2761946,
|
||||
"hash": "ceccd235f7a6871628985d294d7799a2"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/README.md": {
|
||||
"mtime": 1776533040.256771,
|
||||
"hash": "d913624f91ab13167e7b2084c15ea3ec"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/server1c.md": {
|
||||
"mtime": 1776533040.259771,
|
||||
"hash": "074469e3b11d518deaef534a78682b02"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/znamenskoye-network-topology.md": {
|
||||
"mtime": 1776454954.6540399,
|
||||
"hash": "069efe059e04d48a73318ad4a60d4659"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/agentdvr-home.md": {
|
||||
"mtime": 1776533040.256771,
|
||||
"hash": "4389dc7a8af5a8eec0e9078cf1543cdf"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/memory-inventory.md": {
|
||||
"mtime": 1776533040.257771,
|
||||
"hash": "7c2cbd84335b1d5c36998ff3381bc184"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/video-surveillance-report.md": {
|
||||
"mtime": 1776533040.259771,
|
||||
"hash": "20a162cad1d26a84cbbf23e77ecb2e52"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/openclaw.md": {
|
||||
"mtime": 1777274101.853409,
|
||||
"hash": "c8544464ec9ce9294f696f3e333cb65d"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/rustdesk-runbook.md": {
|
||||
"mtime": 1777535102.3830266,
|
||||
"hash": "db23daa2ddd9c22087fa84e7d864087f"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/oleg-agent.md": {
|
||||
"mtime": 1776533040.2587712,
|
||||
"hash": "66764bf64eb9c10bd3ba7b9821694b55"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/netbird-inventory.md": {
|
||||
"mtime": 1776533040.257771,
|
||||
"hash": "c33a5181e1ad45b6accb77c77cd83bef"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/clawdbot.md": {
|
||||
"mtime": 1776582820.6267729,
|
||||
"hash": "0e0c17505e600fb3e2e5906cea9c3cea"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/clawdbot-znam.md": {
|
||||
"mtime": 1776582820.6267729,
|
||||
"hash": "c7456b12fd420333c1be65b87ce36561"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/vps-swtest.md": {
|
||||
"mtime": 1776791401.979136,
|
||||
"hash": "708e0875a4692ee4d075e023cf3aeab8"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/spaceweb-dns.md": {
|
||||
"mtime": 1776582820.6277728,
|
||||
"hash": "7be7f83e0cf7e91d5655be9611014564"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/npm-proxy-hosts.md": {
|
||||
"mtime": 1776533040.2587712,
|
||||
"hash": "d4023b30e51211fcd1719c36af5ddcf6"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/gpu-passthrough.md": {
|
||||
"mtime": 1776533040.256771,
|
||||
"hash": "014042fb4d0e68c3d4ce8e306fccb352"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/openwrt-router.md": {
|
||||
"mtime": 1777498202.2161224,
|
||||
"hash": "ca6584ae5ecbc94cb0a48de74c67099f"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/znamenskoye-log.md": {
|
||||
"mtime": 1776866401.9426904,
|
||||
"hash": "e613a7ccbc964f60ef4c613c727c3399"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/gitea.md": {
|
||||
"mtime": 1776533040.256771,
|
||||
"hash": "6af3c6fea7388b1e8529a46ad3ca15dc"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/mailcow-dttb.md": {
|
||||
"mtime": 1776533040.257771,
|
||||
"hash": "736ee5b42265c72908fb1e6d8efadb7c"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/videonablyudenie-znam.md": {
|
||||
"mtime": 1776533040.2607713,
|
||||
"hash": "3ea218acd3308070da6bfbb874b31e27"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/matrix-homelab.md": {
|
||||
"mtime": 1776533040.257771,
|
||||
"hash": "a650a7f600fbb5d7522bd24924820d6a"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/network-topology-diagram.md": {
|
||||
"mtime": 1777874102.1657124,
|
||||
"hash": "46077e02378c57f65b427f6aab0aa075"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/homeassistant.md": {
|
||||
"mtime": 1776533040.257771,
|
||||
"hash": "3c8ee8300606414c61d31fff665391e3"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/network-topology.md": {
|
||||
"mtime": 1776533040.257771,
|
||||
"hash": "9bfd2213d2f1b0c8955086f17a6df1b7"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/nextcloud-talk-bot/README.md": {
|
||||
"mtime": 1776533040.2587712,
|
||||
"hash": "f57c75af81c6a6af6a30f3d99341bf31"
|
||||
},
|
||||
"/root/knowledge-base/projects/dttb/oleg-agent/docker-compose.yml": {
|
||||
"mtime": 1772553250.5505955,
|
||||
"hash": "d765ad412d55282485eec876cda9bc8b"
|
||||
}
|
||||
}
|
||||
@@ -157,7 +157,7 @@ Pre-production audit (2026-04-30) с найденными уязвимостям
|
||||
- **NPM streams через UI/API не публикуются наружу docker-контейнера.** Нужно править compose-файл и пересоздавать контейнер. Compose лежит в `/data/compose/2/docker-compose.yml` на host LXC 103 (мы его положили туда, ранее Portainer хранил только в своём volume). При обновлении stack ВАЖНО: `cd /data/compose/2 && docker compose -p npm up -d` — relative `./data` resolve в `/data/compose/2/data` (где реальные данные NPM). НЕ обновляй stack через Portainer UI — он работает со своей копией compose в `/var/lib/docker/volumes/portainer_data/_data/compose/2/`, может перезаписать. Текущий compose содержит ports: 80, 81, 443, 21115-21119 (TCP+UDP где надо).
|
||||
- **WebClient в браузере** работает на инфра-уровне (wss://remot.dttb.ru:21118/ws/id и :21119/ws/relay → 101 Switching Protocols). TLS termination для 21118/21119 через `/data/compose/2/data/nginx/custom/stream.conf` (custom stream block в NPM с `listen 21118 ssl` + `ssl_certificate /etc/letsencrypt/live/npm-41/...`). Streams 21115/21117/21116-tcp/udp идут через обычные NPM streams (id 38, 39, 40, 43). Чтобы реально использовать — нужны online peers с api-server login. Подключение Mac→Mac через WebClient невозможно (это и есть `MUST_LOGIN`-style ошибка "не удалось подключиться к серверу ретрансляции").
|
||||
- **community-script может пытаться обновить пакеты** — `apt-mark hold` защищает hbbs/hbbr, но если запустить полный re-run скрипта community-scripts, могут быть сюрпризы. Не запускать без необходимости.
|
||||
- **`/proc/loadavg` в LXC = нагрузка хоста**, не контейнера ([[../../../knowledge-base/feedback_lxc_loadavg]] в memory).
|
||||
- **`/proc/loadavg` в LXC = нагрузка хоста**, не контейнера (`feedback_lxc_loadavg` (user memory) в memory).
|
||||
|
||||
## Развёртывание клиентов
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
date: 2026-04-17
|
||||
type: project
|
||||
tags: [dttb]
|
||||
---
|
||||
|
||||
# Server1C — Сервер 1С в Бужарово
|
||||
|
||||
## Подключение
|
||||
- **Публичный IP:** 185.13.47.2 (RDP:3389)
|
||||
- **Netbird IP:** 100.70.75.103 (server1c.netbird.cloud)
|
||||
- **WinRM:** порт 5985, basic auth
|
||||
- **Учётка:** dttb / 1qaz!QAZ
|
||||
- **OS:** Windows Server 2012 R2 (6.3.9600)
|
||||
- **Hostname:** Server1C
|
||||
- **Локация:** Бужарово
|
||||
|
||||
## 1С:Предприятие
|
||||
Три службы агента:
|
||||
1. `1C:Enterprise 8.3 Server Agent` — StartType: Automatic
|
||||
2. `1C:Enterprise 8.3 Server Agent (x86-64)` — StartType: Automatic
|
||||
3. `RagentServer_8327` — версия 8.3.27.1606, StartType: Automatic
|
||||
|
||||
### Решено: конфликт служб при загрузке (2026-04-16)
|
||||
**Проблема:** 3 службы с Automatic стартовали одновременно, боролись за порты 1540/1541.
|
||||
- Служба 8.3.18 (x86) — бинарник удалён, падала с ошибкой "файл не найден"
|
||||
- RagentServer_8327 — дубликат без параметров, таймаут на портах
|
||||
- Рабочая: `1C:Enterprise 8.3 Server Agent (x86-64)` (8.3.27.1606)
|
||||
|
||||
**Решение:** отключены лишние службы (Disabled), оставлена только x86-64.
|
||||
@@ -1,297 +0,0 @@
|
||||
---
|
||||
date: 2026-03-13
|
||||
type: project
|
||||
tags: [dttb, video]
|
||||
---
|
||||
|
||||
# Система видеонаблюдения — Полный отчёт
|
||||
> Дата: 16 февраля 2026
|
||||
> Статус: Все 3 локации подключены
|
||||
|
||||
---
|
||||
|
||||
## Общая архитектура
|
||||
|
||||
Три удалённые локации объединены через VPN-туннели WireGuard к единому VPS-серверу со статическим IP.
|
||||
VPS выступает точкой агрегации: принимает внешние подключения из интернета и пробрасывает их через DNAT к камерам и регистраторам в локальных сетях.
|
||||
|
||||
```
|
||||
┌──────────────────────────────┐
|
||||
│ VPS 89.111.140.86 │
|
||||
│ WireGuard: 10.5.0.1 │
|
||||
│ NetBird: 100.70.93.36 │
|
||||
│ Ubuntu 24.04.3 LTS │
|
||||
└───┬──────────┬──────────┬────┘
|
||||
│ │ │
|
||||
10.5.0.2 10.5.0.3 10.5.0.4
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────────┐
|
||||
│Знаменск29│ │Охотхоз-во│ │Знаменск Home │
|
||||
│Mikrotik │ │Mikrotik │ │ONT→OpenWrt→WG│
|
||||
│1 камера │ │NVR+6 кам │ │UDM Pro → NVR │
|
||||
└──────────┘ └──────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## VPS-сервер
|
||||
|
||||
| Параметр | Значение |
|
||||
|----------|----------|
|
||||
| IP-адрес | 89.111.140.86 |
|
||||
| ОС | Ubuntu 24.04.3 LTS, ядро 6.17.0-14-generic |
|
||||
| SSH | Ключ /Users/ai/Downloads/id_rsa |
|
||||
| WireGuard | wg0, порт 51820/UDP, IP 10.5.0.1/24 |
|
||||
| Публичный ключ WG | v95Qiu4diw2EBghyQK4obptxnJ7EhAAUXxNflMS0DTw= |
|
||||
| NetBird | 100.70.93.36 |
|
||||
| Конфиг WG | /etc/wireguard/wg0.conf |
|
||||
| Правила iptables | /etc/iptables/rules.v4 |
|
||||
|
||||
---
|
||||
|
||||
## Локация 1: Знаменское 29
|
||||
|
||||
### Оборудование
|
||||
|
||||
| Устройство | IP-адрес | Логин / Пароль |
|
||||
|-----------|----------|----------------|
|
||||
| Камера HiWatch (1 шт.) | 192.168.88.42 | admin / 1qaz!QAZ |
|
||||
| Mikrotik | 192.168.88.1 | admin / admin01 |
|
||||
|
||||
### Подключение к VPS
|
||||
|
||||
| Параметр | Значение |
|
||||
|----------|----------|
|
||||
| WireGuard IP | 10.5.0.2 |
|
||||
| Публичный ключ | 5ZFvQNSCyJwRn3IdLrxFzFh+BEFeejA8VzUoV5a++jY= |
|
||||
| Локальная подсеть | 192.168.88.0/24 |
|
||||
|
||||
### Схема
|
||||
|
||||
Простейшая из трёх локаций. Mikrotik поднимает WireGuard-туннель напрямую к VPS.
|
||||
Один роутер, одна камера.
|
||||
|
||||
```
|
||||
Интернет → VPS (89.111.140.86)
|
||||
│ DNAT
|
||||
▼
|
||||
WireGuard туннель
|
||||
│
|
||||
▼
|
||||
Mikrotik (192.168.88.1)
|
||||
│
|
||||
▼
|
||||
Камера HiWatch (192.168.88.42)
|
||||
```
|
||||
|
||||
### Внешний доступ
|
||||
|
||||
| Сервис | Адрес | Проброс на |
|
||||
|--------|-------|-----------|
|
||||
| Веб-интерфейс | http://89.111.140.86:8080 | 192.168.88.42:80 |
|
||||
| SDK (iVMS-4200) | 89.111.140.86:8082 | 192.168.88.42:8000 |
|
||||
| RTSP-поток | 89.111.140.86:8554 | 192.168.88.42:554 |
|
||||
|
||||
RTSP-ссылка: `rtsp://admin:1qaz!QAZ@89.111.140.86:8554/Streaming/Channels/101`
|
||||
|
||||
---
|
||||
|
||||
## Локация 2: Охотхозяйство
|
||||
|
||||
### Оборудование
|
||||
|
||||
| Устройство | IP-адрес | Логин / Пароль |
|
||||
|-----------|----------|----------------|
|
||||
| NVR HiWatch DS-N316(D) | 192.168.8.247 | admin / 1qaz!QAZ |
|
||||
| Mikrotik | 192.168.8.1 | admin / 1qaz!QAZ |
|
||||
| OpenWrt (NetBird хост) | 192.168.8.108 | root / 1qaz!QAZ |
|
||||
| Камера 1 | 192.168.8.2 | — |
|
||||
| Камера 2 | 192.168.8.3 | — |
|
||||
| Камера 3 | 192.168.8.102 | — |
|
||||
| Камера 4 | 192.168.8.110 | — |
|
||||
| Камера 5 | 192.168.8.113 | — |
|
||||
| Камера 6 | 192.168.8.120 | — |
|
||||
|
||||
### Подключение к VPS
|
||||
|
||||
| Параметр | Значение |
|
||||
|----------|----------|
|
||||
| WireGuard IP | 10.5.0.3 |
|
||||
| Публичный ключ | zZ4UoWNwTxBODr8xZmoCREBL2zXJcmdcxKIPGp/xBC8= |
|
||||
| Локальная подсеть | 192.168.8.0/24 |
|
||||
|
||||
### Схема
|
||||
|
||||
Mikrotik поднимает WireGuard-туннель к VPS. OpenWrt обеспечивает NetBird-доступ для удалённого управления.
|
||||
На VPS DNAT пробрасывает порты как NVR, так и каждой камеры индивидуально.
|
||||
|
||||
```
|
||||
Интернет → VPS (89.111.140.86)
|
||||
│ DNAT
|
||||
▼
|
||||
WireGuard туннель
|
||||
│
|
||||
▼
|
||||
Mikrotik (192.168.8.1)
|
||||
│
|
||||
┌─────┼──────────────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
NVR Камеры 1-6 OpenWrt (NetBird)
|
||||
(.247) (.2,.3,.102,.110, (.108)
|
||||
.113,.120)
|
||||
```
|
||||
|
||||
### Внешний доступ к NVR
|
||||
|
||||
| Сервис | Адрес | Проброс на |
|
||||
|--------|-------|-----------|
|
||||
| Веб-интерфейс | http://89.111.140.86:8180 | 192.168.8.247:80 |
|
||||
| SDK (iVMS-4200) | 89.111.140.86:8100 | 192.168.8.247:8000 |
|
||||
| RTSP-поток | 89.111.140.86:8555 | 192.168.8.247:554 |
|
||||
|
||||
RTSP NVR (каналы 1-6):
|
||||
- `rtsp://admin:1qaz!QAZ@89.111.140.86:8555/Streaming/Channels/101`
|
||||
- `rtsp://admin:1qaz!QAZ@89.111.140.86:8555/Streaming/Channels/201`
|
||||
- `rtsp://admin:1qaz!QAZ@89.111.140.86:8555/Streaming/Channels/301`
|
||||
- `rtsp://admin:1qaz!QAZ@89.111.140.86:8555/Streaming/Channels/401`
|
||||
- `rtsp://admin:1qaz!QAZ@89.111.140.86:8555/Streaming/Channels/501`
|
||||
- `rtsp://admin:1qaz!QAZ@89.111.140.86:8555/Streaming/Channels/601`
|
||||
|
||||
### Внешний доступ к камерам напрямую
|
||||
|
||||
| Камера | IP | RTSP-порт VPS | SDK-порт VPS |
|
||||
|--------|-----|--------------|-------------|
|
||||
| 1 | 192.168.8.2 | 8561 | 8201 |
|
||||
| 2 | 192.168.8.3 | 8562 | 8202 |
|
||||
| 3 | 192.168.8.102 | 8563 | 8203 |
|
||||
| 4 | 192.168.8.110 | 8564 | 8204 |
|
||||
| 5 | 192.168.8.113 | 8565 | 8205 |
|
||||
| 6 | 192.168.8.120 | 8566 | 8206 |
|
||||
|
||||
---
|
||||
|
||||
## Локация 3: Знаменское Home
|
||||
|
||||
### Оборудование
|
||||
|
||||
| Устройство | IP-адрес | Логин / Пароль |
|
||||
|-----------|----------|----------------|
|
||||
| NVR HiWatch DS-N316(D) | 192.168.1.123 | admin / 1qaz!QAZ |
|
||||
| Huawei HG8245H (ONT) | 192.168.100.1 | root / admin |
|
||||
| OpenWrt_3 (WAN) | 192.168.100.3 | root / 1qaz!QAZ |
|
||||
| OpenWrt_3 (LAN) | 10.3.0.1 | — |
|
||||
| OpenWrt_3 (NetBird) | 100.70.54.204 | — |
|
||||
| UDM Pro (Ubiquiti) | 10.3.0.175 | SSH: k9gLi2C / xdjM0eQkIeZfmCFBYo9DP |
|
||||
|
||||
| Параметр NVR | Значение |
|
||||
|-------------|----------|
|
||||
| Серийный номер | DS-N316(D)1620250625CCRRGC0949997WCVU |
|
||||
| Прошивка | V4.76.015 (build 250210) |
|
||||
|
||||
### Подключение к VPS
|
||||
|
||||
| Параметр | Значение |
|
||||
|----------|----------|
|
||||
| WireGuard IP | 10.5.0.4 |
|
||||
| Публичный ключ | HRsAUPwDOh+36EoHrXVYY5t6YVdb612N+E+3I+o6RTw= |
|
||||
| Приватный ключ | 4C9B6iHRRARQfFGBoXimIeznJKj8NX7QmUBW3O+pklE= |
|
||||
| Локальные подсети | 192.168.1.0/24, 192.168.100.0/24 |
|
||||
|
||||
### Топология
|
||||
|
||||
Самая сложная из трёх локаций. Провайдерский GPON-терминал Huawei раздаёт интернет.
|
||||
За ним OpenWrt_3 поднимает WireGuard-туннель к VPS. На LAN-стороне OpenWrt_3 стоит
|
||||
Ubiquiti UDM Pro, за которым NVR на подсети 192.168.1.0/24.
|
||||
|
||||
```
|
||||
Интернет
|
||||
│
|
||||
▼
|
||||
Huawei HG8245H ONT (192.168.100.1)
|
||||
│ Провайдерский GPON-терминал
|
||||
│ LAN: 192.168.100.0/24
|
||||
▼
|
||||
OpenWrt_3
|
||||
│ WAN: 192.168.100.3
|
||||
│ LAN: 10.3.0.1
|
||||
│ WireGuard wg0 → VPS (10.5.0.4 ↔ 10.5.0.1)
|
||||
│ AmneziaWG awg0 (10.8.1.7)
|
||||
│ NetBird wt0 (100.70.54.204)
|
||||
│ Podkop + sing-box (обход блокировок)
|
||||
│
|
||||
│ LAN: 10.3.0.0/24
|
||||
▼
|
||||
UDM Pro (10.3.0.175)
|
||||
│ MAC: 9c:05:d6:ac:98:b8
|
||||
│ LAN: 192.168.1.0/24
|
||||
▼
|
||||
NVR HiWatch DS-N316(D) (192.168.1.123)
|
||||
```
|
||||
|
||||
### Цепочка трафика
|
||||
|
||||
`Интернет → VPS (DNAT) → WireGuard → OpenWrt_3 → UDM Pro → NVR`
|
||||
|
||||
### Ключевые настройки
|
||||
|
||||
| Элемент | Настройка |
|
||||
|---------|-----------|
|
||||
| Маршрут на OpenWrt_3 | 192.168.1.0/24 via 10.3.0.175 (UCI persistent) |
|
||||
| Файрвол OpenWrt_3 | Зона wg: masq=1, форвардинг wg↔lan, wg↔wan |
|
||||
| VPS AllowedIPs | 10.5.0.4/32, 192.168.1.0/24, 192.168.100.0/24 |
|
||||
| VPS маршруты | 192.168.1.0/24 via 10.5.0.4, 192.168.100.0/24 via 10.5.0.4 |
|
||||
|
||||
### Внешний доступ к NVR
|
||||
|
||||
| Сервис | Адрес | Проброс на |
|
||||
|--------|-------|-----------|
|
||||
| Веб-интерфейс | http://89.111.140.86:8280 | 192.168.1.123:80 |
|
||||
| SDK (iVMS-4200) | 89.111.140.86:8282 | 192.168.1.123:8000 |
|
||||
| RTSP-поток | 89.111.140.86:8284 | 192.168.1.123:554 |
|
||||
|
||||
RTSP-ссылка: `rtsp://admin:1qaz!QAZ@89.111.140.86:8284/Streaming/Channels/101`
|
||||
|
||||
---
|
||||
|
||||
## Сводная таблица портов VPS
|
||||
|
||||
Все внешние порты доступны по адресу 89.111.140.86:
|
||||
|
||||
| Порт | Протокол | Назначение | Локация |
|
||||
|------|----------|-----------|---------|
|
||||
| 51820 | UDP | WireGuard | Служебный |
|
||||
| | | | |
|
||||
| 8080 | TCP | Веб камеры | Знаменское 29 |
|
||||
| 8082 | TCP+UDP | SDK камеры | Знаменское 29 |
|
||||
| 8554 | TCP | RTSP камеры | Знаменское 29 |
|
||||
| | | | |
|
||||
| 8180 | TCP | Веб NVR | Охотхозяйство |
|
||||
| 8100 | TCP | SDK NVR | Охотхозяйство |
|
||||
| 8555 | TCP | RTSP NVR | Охотхозяйство |
|
||||
| 8201–8206 | TCP | SDK камер 1–6 | Охотхозяйство |
|
||||
| 8561–8566 | TCP | RTSP камер 1–6 | Охотхозяйство |
|
||||
| | | | |
|
||||
| 8280 | TCP | Веб NVR | Знаменское Home |
|
||||
| 8282 | TCP | SDK NVR | Знаменское Home |
|
||||
| 8284 | TCP | RTSP NVR | Знаменское Home |
|
||||
|
||||
---
|
||||
|
||||
## WireGuard-пиры VPS
|
||||
|
||||
| Локация | WG IP | Публичный ключ | AllowedIPs |
|
||||
|---------|-------|---------------|------------|
|
||||
| Знаменское 29 | 10.5.0.2 | 5ZFvQNSCyJwRn3Id... | 10.5.0.2/32, 192.168.88.0/24 |
|
||||
| Охотхозяйство | 10.5.0.3 | zZ4UoWNwTxBODr8x... | 10.5.0.3/32, 192.168.8.0/24 |
|
||||
| Знаменское Home | 10.5.0.4 | HRsAUPwDOh+36EoH... | 10.5.0.4/32, 192.168.1.0/24, 192.168.100.0/24 |
|
||||
|
||||
---
|
||||
|
||||
## Известные особенности
|
||||
|
||||
- **UDM Pro (Знаменское Home):** SSH-логин работает, но Network Application не запускается — веб-интерфейс управления Ubiquiti недоступен
|
||||
- **Podkop/sing-box на OpenWrt_3:** TPROXY может мешать исходящему TCP. При диагностике проблем со связью рекомендуется временно останавливать: `service podkop stop; service sing-box stop`
|
||||
- **NetBird на OpenWrt_3:** Маршрут NIIKN для 192.168.1.0/24 был деселектирован, чтобы не конфликтовать с WireGuard-маршрутом
|
||||
- **Huawei ONT (192.168.100.1):** Иногда показывает "Waiting..." — перезагрузка решает проблему
|
||||
@@ -1,13 +1,18 @@
|
||||
---
|
||||
date: 2026-03-03
|
||||
updated: 2026-05-06
|
||||
type: project
|
||||
tags: [dttb, video]
|
||||
tags: [dttb, video, znamenskoye, surveillance, agentdvr, wireguard]
|
||||
aliases: [видеонаблюдение Знаменское, камеры Знаменское, surveillance, NVR Охотхозяйство, AgentDVR ЧОП, swtest video, VPS 89.111.140.86]
|
||||
---
|
||||
|
||||
# Система видеонаблюдения — Полный отчёт
|
||||
# Система видеонаблюдения — Полный отчёт (3 локации Знаменского)
|
||||
|
||||
> **Дата:** 16 февраля 2026
|
||||
> **Статус:** Все 3 локации подключены
|
||||
> **Дата:** 16 февраля 2026
|
||||
> **Статус:** Все 3 локации подключены
|
||||
> **Объекты:** Знаменское 29, Охотхозяйство, Знаменское Home + AgentDVR ЧОП
|
||||
|
||||
Canonical-документ. Дубль `video-surveillance-report.md` удалён 2026-05-06 — он был более старой неполной версией (без секции AgentDVR ЧОП).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: project
|
||||
status: active
|
||||
tags: [glavtorg, client, windows, "1c"]
|
||||
aliases: [Главторг, glavtorg, GLAVTORG, Volkkent, "Diana RDP", "Ярослав Глаvторг"]
|
||||
---
|
||||
|
||||
# Проект Glavtorg
|
||||
|
||||
## Сервер
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: project
|
||||
status: active
|
||||
tags: [krasnogorsk, client, residential, openwrt, cudy]
|
||||
aliases: [Красногорск, krasnogorsk, Снегири, "Cudy TR3000", "Deco P9"]
|
||||
---
|
||||
|
||||
# Проект Красногорск
|
||||
|
||||
## Описание
|
||||
|
||||
95
projects/lipki/README.md
Normal file
95
projects/lipki/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: active
|
||||
tags: [object, openwrt, cudy, zvenigorod, netbird, residential, client]
|
||||
aliases: [Липки, Lipki, OpenWrt_Lipki, Звенигород, PSP_Network, Антон]
|
||||
---
|
||||
|
||||
# Липки
|
||||
|
||||
Посёлок в **Звенигородском районе** (Московская обл.). Резидентский объект **Антона** — Wi-Fi mesh, Ajax-сигнализация, OpenWrt-роутер. Имя в DHCP (`MBP-Anthony`) — его MacBook. Снято с роутера 2026-05-06.
|
||||
|
||||
## Роутер
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Hostname | `OpenWrt_Lipki` |
|
||||
| Модель | **Cudy TR3000 v1** (mediatek/filogic, aarch64_cortex-a53) |
|
||||
| OpenWrt | 24.10.3 r28872-daca7c049b |
|
||||
| SSH / LuCI | root / `1qaz!QAZ` (тот же, что на homelab) |
|
||||
| Доступ | через NetBird (`100.70.35.234`) — relay через Helsinki |
|
||||
|
||||
## Сеть
|
||||
|
||||
| Слой | Значение |
|
||||
|---|---|
|
||||
| WAN | DHCP на `eth0`, **белый IPv4 `5.101.135.71`**, без CGNAT |
|
||||
| LAN | `192.168.1.0/24`, шлюз `192.168.1.1`, DHCP `.2–.254`, lease 12 ч |
|
||||
| Wi-Fi 2.4 ГГц | SSID **`PSP_Network`** (ch 6, HT20) |
|
||||
| Wi-Fi 5 ГГц | SSID **`OpenWrt`** (ch 40, HE80) — дефолтное имя, стоит переименовать |
|
||||
| NetBird | `100.70.35.234`, agent 0.59.13, группы `All` + `OpenWRT VPN` |
|
||||
| Locality в NetBird | Istra _(NetBird определяет по WAN-IP/AS — реальный адрес в Звенигородском районе)_ |
|
||||
|
||||
WAN — белый IP, прямой DNAT возможен без хаба `swtest.ru`.
|
||||
|
||||
## Клиенты (DHCP, 12 устройств на 2026-05-06 14:00)
|
||||
|
||||
| IP | Имя | Что |
|
||||
|---|---|---|
|
||||
| .14 | `HP8ADD66` | принтер HP |
|
||||
| .31 | _безымянный_ | ? |
|
||||
| .80 | _безымянный_ | ? |
|
||||
| .81 | _безымянный_ | ? |
|
||||
| .137 | `Redmi-Note-13-Pro-5G` | телефон |
|
||||
| .164 | `deco-P9` | TP-Link Deco P9 (mesh-точка) |
|
||||
| .181 | `Domasniinoteatr` | домашний кинотеатр (Smart TV / медиаплеер) |
|
||||
| .191 | `Ajax-001399A3` | хаб Ajax (охранная сигнализация) |
|
||||
| .209 | _безымянный_ | ? |
|
||||
| .234 | `MBP-Anthony` | MacBook Pro Антона (владельца) |
|
||||
| .243 | _безымянный_ | ? |
|
||||
| .244 | _безымянный_ | ? |
|
||||
|
||||
По составу (Ajax, mesh, MacBook, ТВ, принтер) — **жилой дом**, не офис.
|
||||
|
||||
## Источники
|
||||
|
||||
- [[../dttb/netbird-inventory]] — реестр NetBird-пиров
|
||||
- Снято напрямую: `ssh root@100.70.35.234` через LXC 132 (NetBird wt0)
|
||||
|
||||
## Владелец
|
||||
|
||||
**Антон** — клиент. Точный адрес в посёлке и телефон/мессенджер пока не зафиксированы.
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [ ] Точный адрес в посёлке Липки + контакт Антона (телефон / Telegram)
|
||||
- [ ] Провайдер интернета (whois `5.101.135.71` — фоновое)
|
||||
- [ ] Переименовать SSID `OpenWrt` → что-то осмысленное
|
||||
|
||||
## Связанные
|
||||
|
||||
- [[../znamenskoye/README]] — соседний «куст» в Истре, **не путать**
|
||||
- [[../../claude-memory/znamenskoye-ohothozyistvo]] — соседний OpenWrt 100.70.63.67 (Охотхозяйство)
|
||||
- [[../dttb/openwrt-router]] — homelab-роутер, тот же стек (OpenWrt 24.10.3, 1qaz!QAZ)
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [ ] Точный адрес в посёлке Липки и контактное лицо
|
||||
- [ ] Чей объект (личный / клиент / семья / часть Знаменского?)
|
||||
- [ ] Провайдер интернета, CGNAT или белый IP
|
||||
- [ ] Что обслуживает роутер (камеры / IoT / рабочие места)
|
||||
- [ ] LAN-подсеть и DHCP-диапазон
|
||||
- [ ] Пароль/ключ доступа на сам OpenWrt
|
||||
- [ ] Есть ли DNAT через VPS-хаб или доступ только через NetBird
|
||||
- [ ] Почему отдельный объект, не часть [[../znamenskoye/README]]
|
||||
|
||||
## Связанные
|
||||
|
||||
- [[../znamenskoye/README]] — другой объект-«куст» в Истре, **не путать**
|
||||
- [[../../claude-memory/znamenskoye-ohothozyistvo]] — соседний OpenWrt 100.70.63.67 (Охотхозяйство)
|
||||
- [[../dttb/netbird-inventory]] — единственный пока источник по Липкам
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`Липки`, `Lipki`, `OpenWrt_Lipki`, `100.70.35.234`, `5.101.135.71`, `Cudy TR3000`, `PSP_Network`, `Звенигород`, `Звенигородский район`, `посёлок Липки` — повторено явно, чтобы полнотекстовый поиск Максимки гарантированно цеплял этот файл при любом написании.
|
||||
26
projects/mmfb/README.md
Normal file
26
projects/mmfb/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: active
|
||||
tags: [mmfb, client, lionart, proxmox]
|
||||
aliases: [ММФБ, mmfb, "pve LionART", "LionART", "Yuri Vitalievich", "Юрий Витальевич", "DESKTOP-UFULDJQ"]
|
||||
---
|
||||
|
||||
# Проект ММФБ
|
||||
|
||||
Клиентский объект с собственным Proxmox `pve LionART` (NetBird `100.70.128.49`, LAN `10.253.1.253`) и MikroTik LionART `10.253.1.1`. WAN `195.26.30.163`.
|
||||
|
||||
Внутри Proxmox: 1С / Win2025 / OVPN / NPM / AgentDVR.
|
||||
|
||||
Подключённый клиентский ПК — **Юрий Витальевич** (`DESKTOP-UFULDJQ`, Win 11 25H2 после апгрейда 2026-04-29, NetBird `100.70.173.66`).
|
||||
|
||||
## Контакты и хосты
|
||||
|
||||
- [[mikrotik]] — MikroTik LionART
|
||||
- [[proxmox-inventory]] — VM/LXC внутри `pve LionART`
|
||||
- [[yuri-vitalievich]] — клиентский ПК Юрия
|
||||
- [[otchet-yuri-2026-04]] — отчёт о работах за апрель
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`ММФБ`, `mmfb`, `pve LionART`, `LionART`, `Yuri Vitalievich`, `Юрий Витальевич`, `DESKTOP-UFULDJQ`, `MikroTik LionART`, `10.253.1.0/24`.
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: project
|
||||
status: active
|
||||
tags: [niikn, client]
|
||||
aliases: [НИИКН, niikn, "Cloud-NIIKN New niikn.com", pve-niikn, "Kripto-ARM", "DESKTOP-IC5A0K2 M.Maul", "M.Maul", "Maxim Maul", "Максим Мауль"]
|
||||
---
|
||||
|
||||
# Проект НИИКН
|
||||
|
||||
## Инфраструктура
|
||||
|
||||
35
projects/openwrt-4/README.md
Normal file
35
projects/openwrt-4/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: stub
|
||||
tags: [object, openwrt, netbird, client, todo, moscow, anonymous]
|
||||
aliases: [OpenWrt_4, openwrt-4, openwrt4]
|
||||
---
|
||||
|
||||
# OpenWrt_4 — анонимный клиентский OpenWrt в Москве
|
||||
|
||||
**Стаб-заметка**. Имя пира неинформативное (`OpenWrt_4`), нужно выяснить какого клиента он обслуживает.
|
||||
|
||||
## Что известно (из NetBird)
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Имя пира | `OpenWrt_4` |
|
||||
| NetBird IP | `100.70.235.2` |
|
||||
| Локация (NetBird) | Moscow |
|
||||
| OpenWrt | 24.10.3 |
|
||||
| NetBird agent | 0.50.2 |
|
||||
| Группы | `All`, `OpenWRT VPN`, «Все openwrt» |
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [ ] **Кто хозяин?** — главный пробел
|
||||
- [ ] Что роутер обслуживает
|
||||
- [ ] Точный адрес в Москве
|
||||
- [ ] LAN, провайдер, WAN
|
||||
- [ ] Пароль/ключ доступа
|
||||
- [ ] Стоит переименовать пир в NetBird на осмысленное имя
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`OpenWrt_4`, `100.70.235.2`, openwrt-4, openwrt4.
|
||||
35
projects/sergey/README.md
Normal file
35
projects/sergey/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: stub
|
||||
tags: [object, openwrt, netbird, client, todo]
|
||||
aliases: [Sergey, sergey, OpenWrt_Sergey, Одинцово]
|
||||
---
|
||||
|
||||
# Sergey — клиентский OpenWrt
|
||||
|
||||
**Стаб-заметка**, фактов мало. Создан, чтобы Максимка (FTS) не терял этот объект — раньше упоминался ровно один раз в [[../dttb/netbird-inventory]].
|
||||
|
||||
## Что известно (из NetBird)
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Имя пира | `OpenWrt_Sergey` |
|
||||
| NetBird IP | `100.70.110.164` |
|
||||
| Локация (NetBird) | Odintsovo |
|
||||
| OpenWrt | 24.10.3 |
|
||||
| NetBird agent | 0.59.12 |
|
||||
| Группы | `All`, `OpenWRT VPN` |
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [ ] Кто такой Сергей — клиент / друг / семья?
|
||||
- [ ] Точный адрес и контакт
|
||||
- [ ] Тот ли это Сергей, что управляющий [[../znamenskoye/README]] (вероятно НЕТ — там объекты в Истре, этот в Одинцово)
|
||||
- [ ] Что роутер обслуживает (камеры / IoT / рабочие места)
|
||||
- [ ] LAN, провайдер, белый WAN или CGNAT
|
||||
- [ ] Пароль/ключ доступа
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`Sergey`, `OpenWrt_Sergey`, `100.70.110.164`, `Одинцово`.
|
||||
35
projects/vishnevyy-sad/README.md
Normal file
35
projects/vishnevyy-sad/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: stub
|
||||
tags: [object, openwrt, netbird, client, todo, moscow]
|
||||
aliases: [Вишневый сад, "Вишнёвый сад", vishnevyy-sad, Константин, "OpenWrt Вишневый сад ( Константин )"]
|
||||
---
|
||||
|
||||
# Вишнёвый сад (Константин) — клиентский OpenWrt в Москве
|
||||
|
||||
**Стаб-заметка**. Известно явно — объект **Константина**, явно прописано в названии netbird-пира.
|
||||
|
||||
## Что известно (из NetBird)
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Имя пира | `OpenWrt Вишневый сад ( Константин )` |
|
||||
| NetBird IP | `100.70.152.137` |
|
||||
| Локация (NetBird) | Moscow |
|
||||
| OpenWrt | 24.10.3 |
|
||||
| NetBird agent | 0.59.12 |
|
||||
| Группы | `All`, `OpenWRT VPN` |
|
||||
| Владелец | **Константин** (зафиксирован в имени пира) |
|
||||
|
||||
## Открытые вопросы
|
||||
|
||||
- [ ] Точный адрес и контакт Константина (телефон / Telegram)
|
||||
- [ ] Что такое «Вишнёвый сад» — посёлок / ЖК / название объекта?
|
||||
- [ ] Что роутер обслуживает
|
||||
- [ ] LAN, провайдер, WAN
|
||||
- [ ] Пароль/ключ доступа
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`Вишневый сад`, `Вишнёвый сад`, `Константин`, `OpenWrt Вишневый сад`, `100.70.152.137`.
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
type: project
|
||||
status: active
|
||||
tags: [zelenograd, client, retail, kassa, windows]
|
||||
aliases: [Зеленоград, zelenograd, "DESKTOP-6TF496J", "desktop-6tf496j", стройматериалы, касса]
|
||||
---
|
||||
|
||||
# Зеленоград — строительный магазин
|
||||
|
||||
## Хосты
|
||||
|
||||
39
projects/znamenskoye/README.md
Normal file
39
projects/znamenskoye/README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: project
|
||||
status: active
|
||||
tags: [znamenskoye, client, multi-site, video-surveillance, openwrt, mikrotik, wireguard, netbird]
|
||||
aliases: [Знаменское, Znamenskoe, znamenskoye, "Знаменское 29", "Знаменское Home", "Охотхозяйство", "OpenWrt_Znamenskoe_Home", "OpenWrt_ohothozyistvo", "netbird-29-HP", "Сергей знаменское", "89-111-140-86.swtest.ru", "swtest", "VPS 89.111.140.86"]
|
||||
---
|
||||
|
||||
# Знаменское — куст из 3 объектов
|
||||
|
||||
Клиент с тремя площадками. Управляющий — **Сергей** (есть «шеф над ним»). 3 объекта обслуживаются через единый WG-хаб (VPS `89.111.140.86` / `swtest.ru`) + NetBird overlay.
|
||||
|
||||
## Объекты
|
||||
|
||||
| Объект | NetBird IP | Роль | Документ |
|
||||
|---|---|---|---|
|
||||
| Знаменское Home | `OpenWrt_Znamenskoe_Home` 100.70.54.204 | UDM Pro + Cudy + камеры | [[network-topology-diagram]], [[../dttb/znamenskoye-network-topology]] |
|
||||
| Охотхозяйство | `OpenWrt_ohothozyistvo` 100.70.63.67 | Mikrotik LTE + Orange Pi OpenWrt + NVR + 6 камер | [[../../claude-memory/znamenskoye-ohothozyistvo]] |
|
||||
| Знаменское 29 | `netbird-29-HP` 100.70.137.181 | Mikrotik + 1 камера HiWatch | [[../dttb/videonablyudenie-znam]] |
|
||||
| Видеонаблюдение (общий отчёт) | — | архитектура DNAT через VPS | [[../dttb/videonablyudenie-znam]] |
|
||||
| WG-хаб VPS | `89-111-140-86.swtest.ru` 100.70.93.36 | DNAT камер, агрегация WG | [[../dttb/vps-swtest]] |
|
||||
|
||||
## Контакты и контекст
|
||||
|
||||
- **Сергей** — управляющий по объектам
|
||||
- Над ним — «шеф» (имя пока не зафиксировано)
|
||||
- 2026: Олег готовит счёт + прайс, есть «горящие дыры»
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- [[network-topology-diagram]] — Mermaid-схема сети дома
|
||||
- [[../dttb/znamenskoye-network-topology]] — детально UDM/Cudy/камеры
|
||||
- [[../dttb/videonablyudenie-znam]] — canonical отчёт по видеонаблюдению (3 локации)
|
||||
- [[../../claude-memory/znamenskoye-ohothozyistvo]] — топология охотхозяйства
|
||||
- [[../dttb/vps-swtest]] — общий WG-хаб
|
||||
|
||||
## Aliases для FTS
|
||||
|
||||
`Знаменское`, `Znamenskoe`, `znamenskoye`, `Сергей знаменское`, `Охотхозяйство`, `Знаменское 29`, `Знаменское Home`, `OpenWrt_Znamenskoe_Home`, `OpenWrt_ohothozyistvo`, `netbird-29-HP`, `swtest`, `VPS 89.111.140.86` — повторено явно для FTS.
|
||||
205
scripts/kb-objects-audit.py
Normal file
205
scripts/kb-objects-audit.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""kb-objects-audit — еженедельный health-check vault'а.
|
||||
|
||||
Проверки:
|
||||
1. Каждый онлайн-netbird-пир имеет привязку к проекту (через aliases в frontmatter
|
||||
или собственную карточку). Orphans = клиентские машины без описания.
|
||||
2. Каждый projects/<dir>/README.md имеет валидный frontmatter (type, status,
|
||||
aliases как минимум).
|
||||
3. Битые wiki-ссылки `[[...]]` в vault'е (указывают в небытие).
|
||||
4. Дубли по нечёткому совпадению заголовков (опасные близнецы).
|
||||
|
||||
Output: audit/YYYY-MM-DD-objects-audit.md (каждый запуск перезаписывает дневной).
|
||||
|
||||
Запуск ручной или cron weekly:
|
||||
cd ~/knowledge-base && python3 scripts/kb-objects-audit.py
|
||||
"""
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
VAULT = Path(__file__).resolve().parent.parent
|
||||
OBJECTS_MAP = VAULT / "audit/objects-map.json"
|
||||
|
||||
REQUIRED_FRONTMATTER_KEYS = ["type", "status"]
|
||||
PROJECT_FRONTMATTER_KEYS = ["type", "status", "aliases"]
|
||||
|
||||
|
||||
def load_map() -> list:
|
||||
if not OBJECTS_MAP.exists():
|
||||
sys.exit(f"FATAL: нет {OBJECTS_MAP.relative_to(VAULT)} — запусти scripts/kb-objects-map.py сначала")
|
||||
return json.loads(OBJECTS_MAP.read_text())
|
||||
|
||||
|
||||
def parse_fm(text: str) -> dict:
|
||||
m = re.match(r"^---\n(.+?)\n---\n", text, re.S)
|
||||
if not m:
|
||||
return {}
|
||||
fm = {}
|
||||
for line in m.group(1).splitlines():
|
||||
if ":" not in line or line.startswith("#"):
|
||||
continue
|
||||
k, _, v = line.partition(":")
|
||||
fm[k.strip()] = v.strip()
|
||||
return fm
|
||||
|
||||
|
||||
def find_md_files() -> list[Path]:
|
||||
out = []
|
||||
for p in VAULT.rglob("*.md"):
|
||||
rel = p.relative_to(VAULT)
|
||||
if any(part.startswith(".") for part in rel.parts):
|
||||
continue
|
||||
if rel.parts[0] in ("audit",):
|
||||
# audit-отчёты сами не аудируются
|
||||
if "archive" in rel.parts:
|
||||
continue
|
||||
out.append(p)
|
||||
return out
|
||||
|
||||
|
||||
def check_project_frontmatter(objects: list) -> list[str]:
|
||||
issues = []
|
||||
for o in objects:
|
||||
if o["type"] != "project" or not o["file"]:
|
||||
continue
|
||||
path = VAULT / o["file"]
|
||||
if not path.exists():
|
||||
issues.append(f"- `{o['id']}`: file missing — `{o['file']}`")
|
||||
continue
|
||||
fm = parse_fm(path.read_text())
|
||||
missing = [k for k in PROJECT_FRONTMATTER_KEYS if k not in fm]
|
||||
if missing:
|
||||
issues.append(f"- `{o['id']}`: frontmatter missing {missing} — `{o['file']}`")
|
||||
return issues
|
||||
|
||||
|
||||
def check_broken_wikilinks(files: list[Path]) -> list[tuple[str, str, str]]:
|
||||
"""Возвращает [(source, link, reason)] для битых [[...]] ссылок.
|
||||
|
||||
Проверка только полноценных wiki-ссылок [[...]] (двойная скобка с обеих сторон)."""
|
||||
issues = []
|
||||
all_basenames = {f.stem for f in files}
|
||||
all_relpaths = {str(f.relative_to(VAULT)).replace(".md", "") for f in files}
|
||||
# match [[target]] / [[target|alias]] / [[target#anchor]]
|
||||
pat = re.compile(r"\[\[([^\]\|#\n]+?)(?:[\|#][^\]\n]*)?\]\]")
|
||||
for f in files:
|
||||
# пропускаем graphify-плагин output и весь audit/ (само-цитирование, autogen)
|
||||
rel = f.relative_to(VAULT)
|
||||
if "graphify-out" in rel.parts:
|
||||
continue
|
||||
if rel.parts[0] == "audit":
|
||||
continue
|
||||
if str(rel) == "CLAUDE.md":
|
||||
continue # обучающие placeholder'ы вроде [[двойные скобки]]
|
||||
try:
|
||||
text = f.read_text()
|
||||
except Exception:
|
||||
continue
|
||||
for m in pat.finditer(text):
|
||||
target = m.group(1).strip().rstrip("/").removesuffix(".md")
|
||||
if not target or target in (".", ".."):
|
||||
continue
|
||||
# Allow absolute by relpath, basename, or relative-to-source
|
||||
if target in all_relpaths:
|
||||
continue
|
||||
base = target.split("/")[-1]
|
||||
if base in all_basenames:
|
||||
continue
|
||||
src_dir = f.parent.relative_to(VAULT)
|
||||
resolved = str(src_dir / target).replace("./", "")
|
||||
if resolved in all_relpaths:
|
||||
continue
|
||||
issues.append((str(rel), m.group(0), "→ нет такого файла"))
|
||||
return issues
|
||||
|
||||
|
||||
def check_orphans(objects: list) -> list[dict]:
|
||||
return [o for o in objects if o["type"] == "netbird-only"
|
||||
and o["netbird_peers"] and o["netbird_peers"][0].get("online", True)]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
objects = load_map()
|
||||
files = find_md_files()
|
||||
|
||||
fm_issues = check_project_frontmatter(objects)
|
||||
orphans = check_orphans(objects)
|
||||
wiki_issues = check_broken_wikilinks(files)
|
||||
|
||||
# счётчики
|
||||
n_proj = sum(1 for o in objects if o["type"] == "project")
|
||||
n_proj_with_fm = n_proj - len(fm_issues)
|
||||
n_orphan_online = len(orphans)
|
||||
n_wiki_broken = len(wiki_issues)
|
||||
score = len(fm_issues) * 5 + n_orphan_online * 2 + n_wiki_broken * 3
|
||||
today = date.today().isoformat()
|
||||
out = VAULT / f"audit/{today}-objects-audit.md"
|
||||
|
||||
md = [
|
||||
"---",
|
||||
f"date: {today}",
|
||||
"type: audit",
|
||||
"source: scripts/kb-objects-audit.py",
|
||||
"tags: [audit, objects, frontmatter, links]",
|
||||
f"score: {score}",
|
||||
"---",
|
||||
"",
|
||||
f"# KB objects audit — {today}",
|
||||
"",
|
||||
f"**Score (меньше = лучше): `{score}`**",
|
||||
"",
|
||||
f"- Проектов с frontmatter: **{n_proj_with_fm}/{n_proj}** ({len(fm_issues)} проблем)",
|
||||
f"- NetBird online-пиров без проектной карточки: **{n_orphan_online}**",
|
||||
f"- Битых wiki-ссылок `[[...]]`: **{n_wiki_broken}**",
|
||||
"",
|
||||
]
|
||||
|
||||
md.extend(["## Frontmatter в projects/", ""])
|
||||
if fm_issues:
|
||||
md.extend(fm_issues)
|
||||
else:
|
||||
md.append("✅ все проекты имеют валидный frontmatter")
|
||||
md.append("")
|
||||
|
||||
md.extend(["## Online netbird-пиры без проектной карточки",
|
||||
"",
|
||||
"Эти пиры онлайн в NetBird, но не привязаны ни к одной projects/-странице. ",
|
||||
"Бот не сможет ответить «найди X» осмысленно — нет файла или alias.",
|
||||
"",
|
||||
"Лечение: либо создать stub в `projects/<slug>/README.md` (см. `projects/lipki/` как образец), ",
|
||||
"либо добавить имя пира как полную строку в `aliases` подходящего проекта.",
|
||||
"",
|
||||
"| NetBird-имя | IP | OS | Город |",
|
||||
"|---|---|---|---|"])
|
||||
if orphans:
|
||||
for o in orphans:
|
||||
p = o["netbird_peers"][0]
|
||||
md.append(f"| `{p['name']}` | {p['ip']} | {p.get('os','')} | {p.get('city','')} |")
|
||||
else:
|
||||
md.append("| — | — | — | — |")
|
||||
md.append("")
|
||||
md.append("✅ все онлайн-пиры покрыты")
|
||||
md.append("")
|
||||
|
||||
md.extend(["## Битые wiki-ссылки", ""])
|
||||
if wiki_issues:
|
||||
for src, link, reason in wiki_issues[:50]:
|
||||
md.append(f"- [{src}]({src}) — `{link}` {reason}")
|
||||
if len(wiki_issues) > 50:
|
||||
md.append(f"- ... ещё {len(wiki_issues)-50} (truncated до 50)")
|
||||
else:
|
||||
md.append("✅ битых ссылок не найдено")
|
||||
md.append("")
|
||||
|
||||
out.write_text("\n".join(md))
|
||||
print(f"Wrote {out.relative_to(VAULT)} (score={score})")
|
||||
print(f" frontmatter issues: {len(fm_issues)}")
|
||||
print(f" orphan online peers: {n_orphan_online}")
|
||||
print(f" broken wiki links: {n_wiki_broken}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
240
scripts/kb-objects-map.py
Normal file
240
scripts/kb-objects-map.py
Normal file
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env python3
|
||||
"""kb-objects-map — собирает машиночитаемый реестр объектов и хостов.
|
||||
|
||||
Источники:
|
||||
projects/dttb/netbird-inventory.md — netbird-пиры (источник правды по железу)
|
||||
projects/<dir>/README.md — frontmatter каждого проекта (aliases, tags, status)
|
||||
projects/<file>.md — singleton-проекты
|
||||
|
||||
Output:
|
||||
audit/objects-map.json — структура для бота / structured-поиска
|
||||
projects/_index.md — человеко-читаемый индекс с wiki-ссылками
|
||||
|
||||
Запускать вручную или cron:
|
||||
cd ~/knowledge-base && python3 scripts/kb-objects-map.py
|
||||
"""
|
||||
from datetime import date, datetime
|
||||
from pathlib import Path
|
||||
import json
|
||||
import re
|
||||
|
||||
VAULT = Path(__file__).resolve().parent.parent
|
||||
INV = VAULT / "projects/dttb/netbird-inventory.md"
|
||||
JSON_OUT = VAULT / "audit/objects-map.json"
|
||||
MD_OUT = VAULT / "projects/_index.md"
|
||||
|
||||
|
||||
def parse_frontmatter(text: str) -> dict:
|
||||
m = re.match(r"^---\n(.+?)\n---\n", text, re.S)
|
||||
if not m:
|
||||
return {}
|
||||
fm = {}
|
||||
for line in m.group(1).splitlines():
|
||||
if ":" not in line or line.startswith("#"):
|
||||
continue
|
||||
k, _, v = line.partition(":")
|
||||
k, v = k.strip(), v.strip()
|
||||
if v.startswith("[") and v.endswith("]"):
|
||||
v = [x.strip().strip("\"'") for x in v[1:-1].split(",") if x.strip()]
|
||||
elif v.startswith('"') and v.endswith('"'):
|
||||
v = v[1:-1]
|
||||
fm[k] = v
|
||||
return fm
|
||||
|
||||
|
||||
ROW_RE = re.compile(
|
||||
r"^\|\s*([^|]+?)\s*\|\s*(\d+\.\d+\.\d+\.\d+)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|"
|
||||
)
|
||||
|
||||
|
||||
def parse_netbird(path: Path) -> list[dict]:
|
||||
"""Парсит online + offline-таблицы netbird-inventory.md."""
|
||||
if not path.exists():
|
||||
return []
|
||||
text = path.read_text()
|
||||
parts = re.split(r"^##\s+(?:Оффлайн|Offline)\b.*$", text, maxsplit=1, flags=re.M)
|
||||
online_text, offline_text = parts[0], parts[1] if len(parts) > 1 else ""
|
||||
|
||||
peers = []
|
||||
for line in online_text.splitlines():
|
||||
m = ROW_RE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
name = m.group(1).strip()
|
||||
if name in ("Имя", "---"):
|
||||
continue
|
||||
peers.append({
|
||||
"name": name, "ip": m.group(2).strip(),
|
||||
"os": m.group(3).strip(), "city": m.group(4).strip(),
|
||||
"version": m.group(5).strip(), "online": True,
|
||||
})
|
||||
# offline-таблица: | Имя | IP | ОС | Последний раз онлайн | Город |
|
||||
for line in offline_text.splitlines():
|
||||
m = ROW_RE.match(line)
|
||||
if not m:
|
||||
continue
|
||||
name = m.group(1).strip()
|
||||
if name in ("Имя", "---"):
|
||||
continue
|
||||
peers.append({
|
||||
"name": name, "ip": m.group(2).strip(),
|
||||
"os": m.group(3).strip(),
|
||||
"last_seen": m.group(4).strip(), "city": m.group(5).strip(),
|
||||
"version": "", "online": False,
|
||||
})
|
||||
return peers
|
||||
|
||||
|
||||
def _norm(s: str) -> str:
|
||||
"""Нормализация для нечёткого сравнения. ye→e уравнивает Знаменское/Znamenskoe."""
|
||||
return (s or "").lower().replace("ye", "e").replace(" ", "").replace("_", "").replace("-", "")
|
||||
|
||||
|
||||
def name_match(peer_name: str, candidates: list[str]) -> bool:
|
||||
"""Exact match с нормализацией (ye→e, без пробелов/_/−).
|
||||
Чтобы избежать FP типа `cloud` ⊂ `Cloud-NIIKN`, требуется точное равенство."""
|
||||
pn = _norm(peer_name)
|
||||
if not pn:
|
||||
return False
|
||||
return any(_norm(c) == pn for c in candidates if c)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
peers = parse_netbird(INV)
|
||||
objects = []
|
||||
seen_files = set()
|
||||
|
||||
project_dirs = sorted(p for p in (VAULT / "projects").iterdir() if p.is_dir())
|
||||
for d in project_dirs:
|
||||
readme = d / "README.md"
|
||||
fm = parse_frontmatter(readme.read_text()) if readme.exists() else {}
|
||||
rel_file = f"projects/{d.name}/README.md" if readme.exists() else None
|
||||
aliases = fm.get("aliases", [])
|
||||
if isinstance(aliases, str):
|
||||
aliases = [aliases]
|
||||
names = sorted({d.name, *aliases})
|
||||
nb = [p for p in peers if name_match(p["name"], names)]
|
||||
objects.append({
|
||||
"id": d.name,
|
||||
"type": "project",
|
||||
"names": names,
|
||||
"netbird_peers": nb,
|
||||
"tags": fm.get("tags", []),
|
||||
"owner": fm.get("owner") or fm.get("client"),
|
||||
"region": fm.get("region"),
|
||||
"status": fm.get("status", "unknown"),
|
||||
"file": rel_file,
|
||||
})
|
||||
if rel_file:
|
||||
seen_files.add(rel_file)
|
||||
|
||||
for f in sorted((VAULT / "projects").glob("*.md")):
|
||||
if f.name == "_index.md":
|
||||
continue
|
||||
rel = f"projects/{f.name}"
|
||||
if rel in seen_files:
|
||||
continue
|
||||
fm = parse_frontmatter(f.read_text())
|
||||
objects.append({
|
||||
"id": f.stem,
|
||||
"type": "project-note",
|
||||
"names": [f.stem],
|
||||
"netbird_peers": [],
|
||||
"tags": fm.get("tags", []),
|
||||
"owner": fm.get("owner"),
|
||||
"region": fm.get("region"),
|
||||
"status": fm.get("status", "unknown"),
|
||||
"file": rel,
|
||||
})
|
||||
|
||||
matched_peer_names = {p["name"] for o in objects for p in o["netbird_peers"]}
|
||||
for p in peers:
|
||||
if p["name"] in matched_peer_names:
|
||||
continue
|
||||
objects.append({
|
||||
"id": p["name"].replace(" ", "_"),
|
||||
"type": "netbird-only",
|
||||
"names": [p["name"]],
|
||||
"netbird_peers": [p],
|
||||
"tags": [],
|
||||
"owner": None,
|
||||
"region": p["city"] or None,
|
||||
"status": "no-project-page",
|
||||
"file": None,
|
||||
})
|
||||
|
||||
JSON_OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
JSON_OUT.write_text(json.dumps(objects, indent=2, ensure_ascii=False) + "\n")
|
||||
|
||||
today = date.today().isoformat()
|
||||
now = datetime.now().isoformat(timespec="minutes")
|
||||
n_proj = sum(1 for o in objects if o["type"] in ("project", "project-note"))
|
||||
n_with_nb = sum(1 for o in objects if o["type"] == "project" and o["netbird_peers"])
|
||||
n_orphan = sum(1 for o in objects if o["type"] == "netbird-only")
|
||||
|
||||
md = [
|
||||
"---",
|
||||
f"date: {today}",
|
||||
"type: index",
|
||||
"source: scripts/kb-objects-map.py",
|
||||
"tags: [index, registry, objects, netbird]",
|
||||
"---",
|
||||
"",
|
||||
"# Реестр объектов и netbird-пиров",
|
||||
"",
|
||||
f"Авто-сгенерировано `{now}` из [[dttb/netbird-inventory]] + frontmatter в `projects/`.",
|
||||
"**Не править вручную** — перепишется. Источник правды — frontmatter в каждом README.",
|
||||
"",
|
||||
f"- Проектов: **{n_proj}**, из них с netbird-привязкой: **{n_with_nb}**",
|
||||
f"- NetBird-пиров без projects-страницы: **{n_orphan}** (TODO — создать стабы)",
|
||||
"",
|
||||
"## Проекты с netbird-привязкой",
|
||||
"",
|
||||
"| ID | Имена | NetBird IP | OS | Город | Файл | Статус |",
|
||||
"|---|---|---|---|---|---|---|",
|
||||
]
|
||||
for o in sorted(objects, key=lambda x: x["id"]):
|
||||
if o["type"] == "project" and o["netbird_peers"]:
|
||||
nb_ip = ", ".join(p["ip"] for p in o["netbird_peers"])
|
||||
nb_os = ", ".join(sorted({p["os"] for p in o["netbird_peers"] if p["os"]}))
|
||||
nb_city = ", ".join(sorted({p["city"] for p in o["netbird_peers"] if p["city"]}))
|
||||
file_link = f"[[{o['file'][:-3]}]]" if o["file"] else "—"
|
||||
names = ", ".join(o["names"][:5])
|
||||
md.append(f"| {o['id']} | {names} | {nb_ip} | {nb_os} | {nb_city} | {file_link} | {o['status']} |")
|
||||
|
||||
md.extend([
|
||||
"",
|
||||
"## Проекты без netbird-привязки",
|
||||
"",
|
||||
"| ID | Тип | Файл | Статус |",
|
||||
"|---|---|---|---|",
|
||||
])
|
||||
for o in sorted(objects, key=lambda x: x["id"]):
|
||||
if o["type"] in ("project", "project-note") and not o["netbird_peers"]:
|
||||
file_link = f"[[{o['file'][:-3]}]]" if o["file"] else "—"
|
||||
md.append(f"| {o['id']} | {o['type']} | {file_link} | {o['status']} |")
|
||||
|
||||
md.extend([
|
||||
"",
|
||||
"## NetBird-пиры без projects-страницы — TODO",
|
||||
"",
|
||||
"Эти пиры есть в инвентаре, но у них нет своей карточки в `projects/`. Бот не сможет ответить на «найди мне X» — нет файла. Нужно создать стабы (Фаза 4 плана).",
|
||||
"",
|
||||
"| Имя в NetBird | IP | OS | Город | Версия |",
|
||||
"|---|---|---|---|---|",
|
||||
])
|
||||
for o in sorted(objects, key=lambda x: x["id"]):
|
||||
if o["type"] == "netbird-only":
|
||||
p = o["netbird_peers"][0]
|
||||
md.append(f"| `{p['name']}` | {p['ip']} | {p['os']} | {p['city']} | {p['version']} |")
|
||||
|
||||
md.append("")
|
||||
MD_OUT.write_text("\n".join(md))
|
||||
|
||||
print(f"Wrote {JSON_OUT.relative_to(VAULT)} ({len(objects)} entries: "
|
||||
f"{n_proj} projects, {n_orphan} netbird-only)")
|
||||
print(f"Wrote {MD_OUT.relative_to(VAULT)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -163,7 +163,7 @@ vpn://AAAIYXjanVVdcpswEH7PKTyevjl1QNiAM5MH100T4vwY06ZpQscjg-yoIYKAbMfN5Ay9RI_Qh8
|
||||
|
||||
### Как отозвать у Ярослава
|
||||
|
||||
1. SSH на сервер (см. [[../../projects/dttb/vpn-clients]] → Finland → [[feedback_finland_security]])
|
||||
1. SSH на сервер (см. [[../../projects/dttb/vpn-clients]] → Finland → `feedback_finland_security` (user memory))
|
||||
2. Найти peer Ярослава в `/opt/amnezia/…/wg0.conf` или в amnezia-docker-контейнере
|
||||
3. Удалить `[Peer]` блок → `wg syncconf wg0 <(wg-quick strip wg0)`
|
||||
4. Отметить в таблице: «Отозван: YYYY-MM-DD»
|
||||
|
||||
126
snippets/omniroute-models-audit.md
Normal file
126
snippets/omniroute-models-audit.md
Normal file
@@ -0,0 +1,126 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: snippet
|
||||
tags: [omniroute, claude, opus, smoke-test, audit, lxc-132]
|
||||
---
|
||||
|
||||
# OmniRoute — аудит живости моделей (smoke-test)
|
||||
|
||||
Каталог OmniRoute (`/v1/models`) перечисляет все модели, **подключенные** к провайдерам, но это не значит что они живые. Реальная проверка — пинг через `/v1/chat/completions` с `max_tokens: 10`. Пригождается каждые 1-2 недели или после `npm install omniroute@latest` чтобы убедиться что важные источники Opus/Sonnet не отвалились.
|
||||
|
||||
См. также: [[../decisions/2026-05-06-openclaw-opus-4-7-via-max-cliproxy]] — последний полный аудит и результаты.
|
||||
|
||||
## Где живёт OmniRoute
|
||||
|
||||
| Артефакт | Путь / адрес |
|
||||
|----------|--------------|
|
||||
| LXC | 132, hostname `code-server`, IP `10.0.0.179` |
|
||||
| Service | `systemctl status omniroute` |
|
||||
| Cmd | `/usr/bin/node /root/.npm/_npx/cb5891f90ae65d14/node_modules/omniroute/app/server.js` |
|
||||
| EnvFile | `/root/OmniRoute/.env` |
|
||||
| Repo | `/root/OmniRoute/` (Git) |
|
||||
| Storage | `/root/.omniroute/storage.sqlite` |
|
||||
| Local API | `http://localhost:20128` (порт из `.env` PORT=20128) |
|
||||
| Public | `https://ai.dttb.ru` (через NPM, требует Bearer) |
|
||||
|
||||
## Получить API key
|
||||
|
||||
```bash
|
||||
sshpass -p '1qaz!QAZ' ssh root@10.0.0.179 \
|
||||
"sqlite3 /root/.omniroute/storage.sqlite \"SELECT id, key, name FROM api_keys;\""
|
||||
```
|
||||
|
||||
Использовать `name=test-key` для тестов чтобы не задеть production `claw`.
|
||||
|
||||
## Список моделей
|
||||
|
||||
```bash
|
||||
KEY='<key-из-sqlite>'
|
||||
sshpass -p '1qaz!QAZ' ssh root@10.0.0.179 \
|
||||
"curl -s -H 'Authorization: Bearer $KEY' http://localhost:20128/v1/models"
|
||||
```
|
||||
|
||||
Парсинг с фильтром по подстроке (например все `opus`):
|
||||
|
||||
```bash
|
||||
... | python3 -c '
|
||||
import sys, json
|
||||
d = json.load(sys.stdin)
|
||||
models = d.get("data", d) if isinstance(d, dict) else d
|
||||
for m in models:
|
||||
if "opus" in m["id"].lower():
|
||||
print(m["id"])
|
||||
'
|
||||
```
|
||||
|
||||
## Smoke test одной модели
|
||||
|
||||
```bash
|
||||
KEY='<api-key>'
|
||||
MODEL='cc/claude-opus-4-7'
|
||||
|
||||
curl -s --max-time 30 -X POST http://localhost:20128/v1/chat/completions \
|
||||
-H "Authorization: Bearer $KEY" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"model": "'$MODEL'",
|
||||
"messages": [{"role":"user","content":"Reply only: ok"}],
|
||||
"max_tokens": 10,
|
||||
"stream": false
|
||||
}' | python3 -m json.tool
|
||||
```
|
||||
|
||||
Ожидание `choices[0].message.content` = `"ok"` (или похожее короткое). Если streaming возвращается даже при `stream:false` — это чистый прокси-bypass от Claude Code OAuth, тоже норм.
|
||||
|
||||
## Batch smoke test нескольких моделей
|
||||
|
||||
```bash
|
||||
sshpass -p '1qaz!QAZ' ssh root@10.0.0.179 "
|
||||
KEY='<api-key>'
|
||||
URL='http://localhost:20128/v1/chat/completions'
|
||||
|
||||
for model in 'cc/claude-opus-4-7' 'cc/claude-opus-4-6' 'claude/claude-opus-4-7' 'kr/claude-opus-4.6' 'gh/claude-opus-4.6' 'antigravity/claude-opus-4-6-thinking'; do
|
||||
echo '--- '\$model' ---'
|
||||
curl -s --max-time 25 -X POST \"\$URL\" \\
|
||||
-H \"Authorization: Bearer \$KEY\" \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-d '{\"model\":\"'\$model'\",\"messages\":[{\"role\":\"user\",\"content\":\"Reply ok\"}],\"max_tokens\":10}' \\
|
||||
| head -c 350
|
||||
echo ''
|
||||
done
|
||||
"
|
||||
```
|
||||
|
||||
## Как читать ошибки
|
||||
|
||||
| Сообщение | Что значит | Как чинить |
|
||||
|-----------|------------|-----------|
|
||||
| `Unauthorized` / `Missing API key` | Нет Bearer header или ключ отозван | Достать новый из `api_keys` |
|
||||
| `[400]: The requested model is not available for integrator "vscode-chat"` | MS GitHub Copilot integrator не отдаёт эту модель | Использовать другой источник; проверить какие модели MS сейчас выдаёт (в самом сообщении список Available) |
|
||||
| `[400]: The requested model is not supported` | OmniRoute шлёт ID который провайдер не понимает (часто `4.6` vs `4-6`) | Баг mapping в OmniRoute → `npm install omniroute@latest` или вручную писать поддерживаемый ID |
|
||||
| `[400]: Invalid model. Please select a different model` | Провайдер (Kiro/Kilo) не выдаёт эту модель в их каталоге | Использовать другую модель этого провайдера (у Kiro есть только Sonnet) |
|
||||
| `Missing Google projectId for Antigravity account. Please reconnect OAuth in Providers → Antigravity` | OAuth-сессия Google Antigravity без активного Cloud Code project | Зайти на [antigravity.google](https://antigravity.google), создать project, OmniRoute Dashboard → Providers → Antigravity → Reconnect |
|
||||
| Empty response | Провайдер не отвечает / dead | Проверить аккаунт/ключ провайдера в OmniRoute Dashboard |
|
||||
| `401` от Anthropic | CLIProxy OAuth протух (Claude Code session) | OmniRoute Dashboard → Providers → Claude → Reconnect |
|
||||
|
||||
## Логи OmniRoute
|
||||
|
||||
```bash
|
||||
sshpass -p '1qaz!QAZ' ssh root@10.0.0.179 "journalctl -u omniroute --since '1 hour ago' --no-pager | tail -50"
|
||||
```
|
||||
|
||||
Ключевые маркеры в логах:
|
||||
- `[USAGE] <PROVIDER>` — фактические вызовы (KIRO, CC, CLAUDE, ANTIGRAVITY...)
|
||||
- `model fallback decision` — failover включился
|
||||
- `Account quota exceeded` — pool аккаунта провайдера исчерпан
|
||||
- `circuit breaker open` — провайдер временно отключён OmniRoute из-за серии ошибок
|
||||
- `MEMORY_EXTRACTION` — Memory Tool через OmniRoute
|
||||
|
||||
## Известные «вечные» гнилые модели (на 2026-05-06)
|
||||
|
||||
Не тратить на них тесты, пока OmniRoute не обновится:
|
||||
- `gh/claude-opus-4.6` — MS убрала из integrator scope
|
||||
- `gh/claude-opus-4.7` — баг ID mapping
|
||||
- `kr/claude-opus-*` / `kiro/claude-opus-*` — Kiro Opus не выдаёт в принципе
|
||||
- `kc/anthropic/*` / `kilocode/anthropic/*` — провайдеры мёртвы / не настроены
|
||||
- `antigravity/claude-opus-4-6-thinking` — пока OAuth projectId не подключен
|
||||
218
snippets/openclaw-kb-webhook.md
Normal file
218
snippets/openclaw-kb-webhook.md
Normal file
@@ -0,0 +1,218 @@
|
||||
---
|
||||
date: 2026-05-06
|
||||
type: snippet
|
||||
tags: [openclaw, gitea, webhook, kb-sync]
|
||||
status: deployed
|
||||
---
|
||||
|
||||
# Webhook Gitea → kb-pull на LXC 137 (Максимка)
|
||||
|
||||
Push с Mac → видно боту в FTS за **~11 секунд** (было 5–15 минут через cron `*/15`). Развёрнуто 2026-05-06. Подробности — [[../decisions/2026-05-06-openclaw-kb-webhook-deployment]].
|
||||
|
||||
Cron `*/15` оставлен safety net — спасает, если listener умер или Gitea недоступна.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```
|
||||
Mac git push ──► Gitea (LXC 136, 10.0.0.189)
|
||||
│ webhook POST + HMAC-SHA256
|
||||
▼
|
||||
LXC 137 :18790 kb-pull-webhook.service
|
||||
│ subprocess.Popen
|
||||
▼
|
||||
/usr/local/bin/kb-pull.sh
|
||||
│ git fetch + ff-only / auto-reset
|
||||
│ if HEAD changed → openclaw memory index
|
||||
▼
|
||||
FTS реиндекс ~38 сек на 875 файлов / 1791 chunk
|
||||
```
|
||||
|
||||
## 1. Listener `/usr/local/bin/kb-pull-webhook.py`
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Listens for Gitea push webhooks, triggers kb-pull.sh."""
|
||||
import hmac, hashlib, http.server, os, subprocess, sys
|
||||
|
||||
SECRET = os.environ.get("GITEA_WEBHOOK_SECRET", "").encode()
|
||||
PULL = "/usr/local/bin/kb-pull.sh"
|
||||
|
||||
class H(http.server.BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
n = int(self.headers.get("Content-Length", 0) or 0)
|
||||
body = self.rfile.read(n)
|
||||
sys.stderr.write("WH POST event=%s sig=%s len=%d\n" % (
|
||||
self.headers.get("X-Gitea-Event", "-"),
|
||||
(self.headers.get("X-Gitea-Signature", "-") or "-")[:12],
|
||||
n)); sys.stderr.flush()
|
||||
if SECRET:
|
||||
sig = self.headers.get("X-Gitea-Signature", "")
|
||||
mac = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
|
||||
if not hmac.compare_digest(sig, mac):
|
||||
sys.stderr.write("WH 401 sig-mismatch\n"); sys.stderr.flush()
|
||||
self.send_error(401); return
|
||||
subprocess.Popen([PULL], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
self.send_response(202); self.end_headers()
|
||||
self.wfile.write(b"queued\n")
|
||||
sys.stderr.write("WH 202 kb-pull launched\n"); sys.stderr.flush()
|
||||
def do_GET(self): # health-check
|
||||
self.send_response(200); self.end_headers()
|
||||
self.wfile.write(b"ok\n")
|
||||
def log_message(self, fmt, *a): # стандартный access log в journal
|
||||
sys.stderr.write("WH-base " + (fmt % a) + "\n"); sys.stderr.flush()
|
||||
|
||||
if __name__ == "__main__":
|
||||
port = int(os.environ.get("PORT", "18790"))
|
||||
sys.stderr.write("WH listener up on :%d secret=%s\n" % (port, "yes" if SECRET else "NO"))
|
||||
sys.stderr.flush()
|
||||
http.server.HTTPServer(("0.0.0.0", port), H).serve_forever()
|
||||
```
|
||||
|
||||
```bash
|
||||
chmod +x /usr/local/bin/kb-pull-webhook.py
|
||||
```
|
||||
|
||||
## 2. systemd unit `/etc/systemd/system/kb-pull-webhook.service`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=KB pull webhook listener (Gitea -> kb-pull.sh)
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=GITEA_WEBHOOK_SECRET=<openssl rand -hex 32>
|
||||
Environment=PORT=18790
|
||||
ExecStart=/usr/bin/python3 /usr/local/bin/kb-pull-webhook.py
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
sed -i "s|<openssl rand -hex 32>|$SECRET|" /etc/systemd/system/kb-pull-webhook.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now kb-pull-webhook.service
|
||||
ss -ltnp | grep 18790 # должен слушать на 0.0.0.0:18790
|
||||
```
|
||||
|
||||
## 3. Хук в `kb-pull.sh` — реиндекс при новом HEAD
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# read-only pull knowledge-base for openclaw context
|
||||
# Auto-reset at divergence; reindex FTS on new commits.
|
||||
set -u
|
||||
exec 9>/tmp/kb-pull.lock
|
||||
flock -w 180 9 || exit 0 # ← ждать до 3 мин, не -n (race на двойном webhook)
|
||||
cd /root/knowledge-base
|
||||
|
||||
LOG=/var/log/kb-pull.log
|
||||
{
|
||||
echo "--- $(date -Iseconds) ---"
|
||||
HEAD_BEFORE=$(git rev-parse HEAD 2>/dev/null)
|
||||
git fetch --quiet origin main
|
||||
if ! git merge-base --is-ancestor HEAD origin/main 2>/dev/null; then
|
||||
echo "divergence detected, resetting to origin/main"
|
||||
git reset --hard origin/main
|
||||
else
|
||||
git pull --ff-only --quiet origin main
|
||||
fi
|
||||
HEAD_AFTER=$(git rev-parse HEAD 2>/dev/null)
|
||||
if [ "$HEAD_BEFORE" != "$HEAD_AFTER" ]; then
|
||||
echo "HEAD: $HEAD_BEFORE -> $HEAD_AFTER, triggering FTS reindex"
|
||||
timeout 120 openclaw memory index 2>&1 | tail -5
|
||||
else
|
||||
echo "no new commits, skip reindex"
|
||||
fi
|
||||
} >> $LOG 2>&1
|
||||
```
|
||||
|
||||
`flock -w 180` — критично. Если webhook прилетает во время предыдущего pull/reindex (быстрые двойные push), `-n` молча выходит и пропускает push.
|
||||
|
||||
## 4. Регистрация webhook в Gitea (через API)
|
||||
|
||||
```bash
|
||||
SECRET=... # тот же, что в systemd unit
|
||||
curl -u oleg:OL260380eg -X POST http://10.0.0.189:3000/api/v1/repos/oleg/knowledge-base/hooks \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"type\":\"gitea\",\"config\":{\"url\":\"http://10.0.0.239:18790/\",\"content_type\":\"json\",\"secret\":\"$SECRET\"},\"events\":[\"push\"],\"active\":true,\"branch_filter\":\"main\"}"
|
||||
```
|
||||
|
||||
Через UI: `git.dttb.ru` → репо → Settings → Webhooks → Add → Gitea, заполнить теми же полями.
|
||||
|
||||
## 5. **Обязательно:** разрешить private IP в Gitea
|
||||
|
||||
По умолчанию Gitea **блокирует webhooks на private адреса** (SSRF-protection). В docker-логах будет:
|
||||
|
||||
> `webhook can only call allowed HTTP servers (check your webhook.ALLOWED_HOST_LIST setting), deny '10.0.0.239'`
|
||||
|
||||
Listener при этом ничего не получит, в его journal пусто. Лечится в `app.ini` Gitea:
|
||||
|
||||
```ini
|
||||
[webhook]
|
||||
ALLOWED_HOST_LIST = 10.0.0.0/24,*.dttb.ru,private
|
||||
```
|
||||
|
||||
Конкретно у нас Gitea в docker, конфиг — `/opt/gitea/data/gitea/conf/app.ini` на LXC 136. После правки:
|
||||
```bash
|
||||
docker restart gitea
|
||||
```
|
||||
|
||||
## 6. Проверка end-to-end
|
||||
|
||||
```bash
|
||||
# на Mac
|
||||
cd ~/knowledge-base
|
||||
git commit --allow-empty -m "smoke" && git push
|
||||
|
||||
# на 137 (через 5–10 сек)
|
||||
pct exec 137 -- journalctl -u kb-pull-webhook.service --since "30 sec ago" -n 5
|
||||
# WH POST event=push sig=... len=...
|
||||
# WH 202 kb-pull launched
|
||||
pct exec 137 -- tail -3 /var/log/kb-pull.log
|
||||
# HEAD: <old> -> <new>, triggering FTS reindex
|
||||
# Memory index updated (main).
|
||||
```
|
||||
|
||||
Измеренный тайминг real push (2026-05-06): T0 git push → T+11s `git rev-parse HEAD` на 137 уже новый.
|
||||
|
||||
## 7. Опционально: ограничить порт по источнику
|
||||
|
||||
Listener слушает `0.0.0.0:18790`. Если нужно жёстко закрыть:
|
||||
|
||||
```bash
|
||||
iptables -A INPUT -p tcp --dport 18790 -s 10.0.0.189 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 18790 -j DROP
|
||||
```
|
||||
|
||||
Не делал — homelab LAN trusted, secret-HMAC уже отсекает левые запросы.
|
||||
|
||||
## Откат
|
||||
|
||||
```bash
|
||||
# на 137
|
||||
systemctl disable --now kb-pull-webhook.service
|
||||
rm /etc/systemd/system/kb-pull-webhook.service /usr/local/bin/kb-pull-webhook.py
|
||||
# восстановить kb-pull.sh из бэкапа
|
||||
cp /usr/local/bin/kb-pull.sh.bak.* /usr/local/bin/kb-pull.sh
|
||||
|
||||
# в Gitea
|
||||
curl -u oleg:OL260380eg -X DELETE http://10.0.0.189:3000/api/v1/repos/oleg/knowledge-base/hooks/<id>
|
||||
```
|
||||
|
||||
Cron `*/15` продолжит работать.
|
||||
|
||||
## Грабли (выписаны из реального деплоя)
|
||||
|
||||
| Симптом | Причина | Где смотреть |
|
||||
|---|---|---|
|
||||
| Test delivery 204 от Gitea, но listener не получил | `webhook.ALLOWED_HOST_LIST` не разрешает private IP | `docker logs gitea` на LXC 136 |
|
||||
| Двойной push — второй пропускается | `flock -n` без ожидания | Заменить на `flock -w 180` |
|
||||
| FTS не обновился, хотя git pull прошёл | `openclaw memory` не дёргается | Хук в `kb-pull.sh` после ff-only |
|
||||
| Listener в restart-loop после правки | sed побил python синтаксис | `journalctl -u kb-pull-webhook --since "1 min ago"` |
|
||||
Reference in New Issue
Block a user