--- date: 2026-05-06 type: snippet tags: [openclaw, gitea, webhook, kb-sync] --- # Webhook Gitea → kb-pull на LXC 137 (Максимка) Сократить лаг Mac → openclaw FTS с **до 15 мин** (cron `*/15`) до **<5 секунд** через push-webhook от Gitea. Cron оставляем как safety net — он почти ничего не стоит и спасает, если listener умер. ## Архитектура ``` Mac git push ──► Gitea (LXC 136, 10.0.0.189) │ │ HTTP POST (push event) ▼ LXC 137 :18790 kb-pull-webhook (systemd) │ └─► /usr/local/bin/kb-pull.sh (тот же что в cron) ``` ## 1. Listener `/usr/local/bin/kb-pull-webhook.py` ```python #!/usr/bin/env python3 """Слушает Gitea webhook, дёргает kb-pull.sh.""" import hmac, hashlib, http.server, os, subprocess 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) if SECRET: sig = self.headers.get('X-Gitea-Signature', '') mac = hmac.new(SECRET, body, hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, mac): 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') 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): # без спама в journal pass if __name__ == '__main__': port = int(os.environ.get('PORT', '18790')) 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=CHANGE_ME_LONG_RANDOM Environment=PORT=18790 ExecStart=/usr/bin/python3 /usr/local/bin/kb-pull-webhook.py Restart=on-failure RestartSec=5 # Минимальные права — listener не пишет в /root, только запускает kb-pull.sh NoNewPrivileges=yes PrivateTmp=yes [Install] WantedBy=multi-user.target ``` ```bash systemctl daemon-reload systemctl enable --now kb-pull-webhook.service systemctl status kb-pull-webhook.service ss -ltnp | grep 18790 ``` Сгенерировать секрет: ```bash openssl rand -hex 32 ``` ## 3. Webhook в Gitea UI `git.dttb.ru` → репо `oleg/knowledge-base` → **Settings → Webhooks → Add Webhook → Gitea** | Поле | Значение | |---|---| | Target URL | `http://10.0.0.239:18790/` | | HTTP Method | POST | | Content Type | `application/json` | | Secret | тот же, что в systemd unit | | Trigger On | Push Events | | Branch filter | `main` | | Active | ✓ | Жмём **Test Delivery** — должно прилететь `202 queued`. ## 4. Опционально: ограничить порт по источнику Listener слушает на `0.0.0.0`. Если хочется — закрыть всё кроме Gitea LXC 136: ```bash iptables -A INPUT -p tcp --dport 18790 -s 10.0.0.189 -j ACCEPT iptables -A INPUT -p tcp --dport 18790 -j DROP # или через nftables / netbird ACL — на вкус ``` ## 5. Проверка end-to-end ```bash # на Mac cd ~/knowledge-base date >> /tmp/test && git add -A && git commit -m "webhook test" && git push # на LXC 137 — должна появиться свежая строка pct exec 137 -- tail -3 /var/log/kb-pull.log ``` ## Откат ```bash systemctl disable --now kb-pull-webhook.service rm /etc/systemd/system/kb-pull-webhook.service rm /usr/local/bin/kb-pull-webhook.py # в Gitea — деактивировать webhook ``` Cron `*/15` остаётся живым — KB продолжит синкаться по таймеру. ## Замечания - **`openclaw memory` не переиндексируется автоматически** после `kb-pull`. Бот видит markdown сразу (он читает из FS), но FTS-индекс в `~/.openclaw/memory/main.sqlite` обновляется при следующем reindex-цикле. Если нужно ускорить — после pull можно дёргать `openclaw memory rebuild --incremental` (проверить наличие команды в текущей версии). Это уже отдельный шаг — добавить в `kb-pull.sh` после успешного `git pull`. - На LXC 137 `kb-pull.sh` — простой ff-only с авто-reset при divergence. Webhook на нём ничего не ломает: тот же скрипт, та же блокировка `/tmp/kb-pull.lock`.