Open WebUI API наружу для Perplexity и alatyr-service¶
Публичный API-endpoint для интеграции Perplexity Computer и server-side помощников
alatyr-serviceс локальным Open WebUI на ragserver.✅ Внедрено 09.08.2026, маршрут обновлён 24.08.2026. Внешний тест из Perplexity Computer: HTTP 200, коллекция
alatyr-infra-kb, 45 файлов,write_access=false. После переоформления peer адрес изменён на10.8.1.12.
Фактическая реализация 09.08.2026¶
- DNS:
rag-api.alatyr-service.ru A 46.17.99.183; AAAA отсутствует. - TLS: Let's Encrypt, сертификат действует до 07.11.2026, автообновление Certbot включено.
- Ragserver: Open WebUI опубликован одновременно на
127.0.0.1:3000и10.8.1.12:3000. - Маршрут VPS: host не имеет интерфейса
10.8.1.x, потому трафик идёт через Docker-контейнерamnezia-awg2(172.29.172.2) и bridgeamn0. - Постоянство маршрута:
/usr/local/sbin/alatyr-rag-api-route.sh+ enabled oneshot unit/etc/systemd/system/alatyr-rag-api-route.service. - SNAT внутри контейнера: только
172.29.172.1/32 → 10.8.1.12/32, выходawg0,MASQUERADE. - nginx:
/etc/nginx/sites-available/rag-api.alatyr-service.ru, наружу проксируется толькоlocation ^~ /api/;/healthzвозвращает 200 без auth,/возвращает 404. - Auth:
X-Edge-Authпроверяется nginx;Authorization: Bearer ...проверяется Open WebUI. - Open WebUI: отдельный обычный пользователь
perplexity-computer, группаAPI Integrations, API Keys permission и read-only grant к knowledgealatyr-infra-kb. - Perplexity custom credential: тип
headers, в единственное поле Headers передаётся один JSON-объект с обоими заголовками. Отдельные поля или строки не работают.
Секреты хранятся в Vaultwarden и Perplexity Credentials, в Git их нет.
Интеграция object assistant в alatyr-service¶
На 27.08.2026 PR alatyr-service #95 развёрнут в production, merge-коммит
b0f97d1. Это второй изолированный клиент API наряду с
perplexity-computer.
Текущий контур¶
- service user Open WebUI:
alatyr-service-assistant; - группа:
API Integrations; - API Keys permission выдаётся группе;
- доступ к модели должен быть ограничен private model wrapper, целевой Model ID
alatyr-object-assistant, базовая локальная модельllama3.2:3b; - общая knowledge
alatyr-infra-kbк object assistant не прикладывается; - CRM-контекст формируется на VPS для одного объекта и передаётся отдельным недоверенным user message;
- ответы сохраняются в
object_assistant_messages; - assistant работает read-only и не меняет CRM;
- контрольный запрос после настройки пока не выполнялся по решению владельца.
Наличие private wrapper, его точный Model ID и grant service user должны быть
подтверждены в рамках CRM-15 до признания интеграции полностью проверенной.
Runtime-конфигурация¶
Systemd-unit alatyr-service подключает:
Там заданы только server-side переменные:
Имена переменных подтверждены в окружении запущенного процесса. Значения
токенов не выводятся и не фиксируются в KB. Для service user используется
отдельная запись Vaultwarden, рекомендуемое имя:
OpenWebUI API · alatyr-service.
Исторические документы могли называть edge-секрет
RAG_API_EDGE_BEARER; фактический код object assistant ожидает
RAG_API_EDGE_TOKEN.
Граница доверия¶
- браузер не получает токены и не обращается к Open WebUI напрямую;
alatyr-serviceотправляет запрос только по HTTPS;- чувствительные response body не попадают в application logs;
- нет общей коллекции клиентских файлов;
- содержимое бинарных документов пока не передаётся, доступны только метаданные;
- будущий OpenAI API подключается только через server-side model router, классификацию данных, лимиты и audit.
Полная дорожная карта ролей, object-scoped RAG, OCR, сметчика, делопроизводителя, секретаря и управляемых действий: rag-openai-business-assistants-roadmap.md.
Задача¶
Дать Perplexity Computer возможность вызывать Open WebUI API (RAG-поиск по KB, чат с локальными LLM) — но так, чтобы:
- Реальный ragserver оставался в LAN, недоступен из интернета напрямую
- Публичный endpoint был на VPS (46.17.99.183) — на неё DPI Инетком не влияет
- Трафик VPS ↔ ragserver шёл через существующий AmneziaWG VPN
- Аутентификация — Bearer token для Perplexity как отдельного клиента (не тот же токен что для rag-sync)
- Только явные изолированные service clients (
perplexity-computerиalatyr-service-assistant) — не выставляем API всему миру и не используем admin-токен
Архитектура¶
Perplexity Computer
│
│ HTTPS + Bearer sk-perplexity-...
▼
┌──────────────────────────────────────┐
│ VPS 46.17.99.183 (публичный) │
│ nginx: rag-api.alatyr-service.ru │
│ → auth (Bearer check) │
│ → rate limit │
│ → proxy_pass │
└──────────┬────────────────────────────┘
│
│ через AmneziaWG (10.8.1.0/24)
│ VPS host → amn0 → amnezia-awg2 → awg0 → ragserver: 10.8.1.12
│
▼
┌──────────────────────────────────────┐
│ ragserver (192.168.1.200) │
│ Open WebUI :3000 │
│ → validates Bearer token │
│ → выполняет запрос │
└──────────────────────────────────────┘
Ключевые идеи:
- Два слоя auth: nginx на VPS проверяет свой Bearer (защита от случайного трафика), Open WebUI проверяет свой Bearer (боевой контроль доступа). Токены разные.
- Отдельный поддомен
rag-api.alatyr-service.ru— чтобы не смешивать сrag.alatyr-service.ru(интерфейс, только через VPN). - DNS-запись
rag-apiрезолвится в публичный IP VPS (46.17.99.183), не в LAN.
Шаг 1 — Проверить связность VPS ↔ ragserver через VPN¶
На VPS (историческая исходная проверка; прямого маршрута с host изначально нет):
Если не пингуется, сначала проверять доступ из контейнера amnezia-awg2. В фактической схеме VPS-host использует отдельный /32 route и SNAT, описанные выше.
- Проверить sudo awg show awg0 — есть ли peer awg-rag
- Проверить sudo systemctl status amneziawg на VPS
- Проверить что на ragserver AmneziaWG-клиент активен: sudo awg show
Проверить Open WebUI слышит на LAN-адресе (не только localhost):
На ragserver:
Ожидание: LISTEN 0 511 *:3000 или 0.0.0.0:3000. Если 127.0.0.1:3000 — docker-контейнер слушает только на loopback, надо править docker-compose.yml.
Из VPS проверить что API отвечает:
Если 200 OK → идём дальше. Если connection refused — Open WebUI слушает только 127.0.0.1, надо открыть на 10.8.1.12.
Как открыть OpenWebUI на VPN-IP (если сейчас только localhost)¶
На ragserver отредактировать /opt/rag-platform/docker-compose.yml, сервис openwebui:
services:
openwebui:
ports:
# было: "127.0.0.1:3000:8080"
# стало (слушать и на loopback для nginx, и на VPN):
- "127.0.0.1:3000:8080" # для локального nginx (интерфейс)
- "10.8.1.12:3000:8080" # для VPS через VPN
Или если проще — публиковать на все интерфейсы:
Но тогда обязательно: sudo ufw deny 3000 наружу, sudo ufw allow from 10.8.1.0/24 to any port 3000.
Применить: sudo docker compose -f /opt/rag-platform/docker-compose.yml up -d openwebui.
Шаг 2 — Создать API-токен в Open WebUI¶
В интерфейсе (фактический безопасный вариант):
1. Администратором создать обычного пользователя perplexity-computer.
2. Создать группу API Integrations, включить только permission API Keys, добавить пользователя.
3. Войти пользователем → Settings → Account → API Keys → создать ключ.
4. Администратором открыть knowledge alatyr-infra-kb → Access → выдать группе/пользователю только Read.
5. Положить API key в Vaultwarden. Не использовать API key администратора и не ротировать ключ rag-sync.
Запись в Vaultwarden¶
| Поле | Значение |
|---|---|
| Name | OpenWebUI API · perplexity |
| Folder | Alatyr / Integrations |
| Username | perplexity |
| Password | sk-... (сам токен) |
| URI 1 | https://rag-api.alatyr-service.ru/ |
Custom fields:
| Field | Value |
|---|---|
client |
perplexity-computer |
owner_user |
admin (кто в Open WebUI создал) |
permissions |
read: knowledge, chat |
created_at |
2026-08-XX |
rotate_after |
2027-02-XX (6 мес) |
Notes:
Токен для Perplexity Computer → Open WebUI API.
Используется в custom-credential Perplexity:
Host: rag-api.alatyr-service.ru
Header: Authorization: Bearer <этот токен>
Через nginx VPS проксируется на ragserver 10.8.1.12:3000.
nginx имеет собственный Bearer для внешней защиты — см. отдельную запись
`OpenWebUI Edge Bearer · perplexity`.
При ротации:
1. Создать новый ключ в Open WebUI → Settings → Account → API Keys
2. Обновить эту запись
3. Обновить custom-credential в Perplexity
4. Удалить старый ключ в Open WebUI
Шаг 3 — Создать Edge Bearer (второй слой на nginx VPS)¶
Это токен которым Perplexity подписывается на nginx VPS. nginx его проверит и только тогда передаст запрос дальше.
На VPS сгенерировать случайную строку:
Результат — что-то вроде sk-edge-8xJKn2p_... (64 символа, url-safe).
Запись в Vaultwarden¶
| Поле | Значение |
|---|---|
| Name | OpenWebUI Edge Bearer · perplexity |
| Folder | Alatyr / Integrations |
| Username | perplexity-edge |
| Password | сгенерированная строка |
| URI 1 | https://rag-api.alatyr-service.ru/ |
Custom fields:
| Field | Value |
|---|---|
env_var_name |
RAG_API_EDGE_BEARER |
nginx_config |
/etc/nginx/sites-enabled/rag-api.alatyr-service.ru |
created_at |
2026-08-XX |
rotate_after |
2027-02-XX |
Notes:
Edge-Bearer для nginx на VPS 46.17.99.183.
nginx проверяет header `X-Edge-Auth: <этот токен>` до того как проксировать
запрос на ragserver. Это защита от:
- Случайных сканов интернета
- Утечки основного OpenWebUI-токена (нужны оба)
Perplexity должен слать ДВА header:
Authorization: Bearer <sk-... из записи OpenWebUI API · perplexity>
X-Edge-Auth: <этот токен>
Шаг 4 — DNS-запись для поддомена¶
В панели Reg.ru (управление доменом alatyr-service.ru):
Добавить A-запись:
- Тип: A
- Имя: rag-api (полный: rag-api.alatyr-service.ru)
- Значение: 46.17.99.183
- TTL: 3600
Проверить резолв:
Пропагация: до 15 минут.
Шаг 5 — Let's Encrypt сертификат¶
На VPS:
Certbot использует HTTP challenge — порт 80 на VPS уже открыт для существующих доменов, ничего дополнительно не нужно.
Проверить:
Ожидание: fullchain.pem и privkey.pem появились. Автообновление через certbot.timer подхватит новый домен автоматически.
Шаг 6 — nginx-конфиг на VPS¶
Создать /etc/nginx/sites-available/rag-api.alatyr-service.ru:
# ═══════════════════════════════════════════════════════════════════
# Public API endpoint для Perplexity Computer → Open WebUI на ragserver
#
# Задача 36. Роутинг:
# Perplexity → HTTPS → VPS nginx → AmneziaWG туннель → ragserver:3000
#
# Два слоя auth:
# 1. nginx: X-Edge-Auth header (Edge Bearer)
# 2. Open WebUI: Authorization: Bearer (нативный API-токен)
# ═══════════════════════════════════════════════════════════════════
# ─── Rate limit zone (отдельная для API) ───
limit_req_zone $binary_remote_addr zone=rag_api:10m rate=30r/m;
# ─── HTTP → HTTPS редирект ───
server {
listen 80;
listen [::]:80;
server_name rag-api.alatyr-service.ru;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# ─── HTTPS основной ───
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name rag-api.alatyr-service.ru;
# ═══ TLS ═══
ssl_certificate /etc/letsencrypt/live/rag-api.alatyr-service.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rag-api.alatyr-service.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# ═══ Security headers ═══
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header X-Robots-Tag "noindex, nofollow" always;
# ═══ Логи ═══
access_log /var/log/nginx/rag-api-access.log;
error_log /var/log/nginx/rag-api-error.log warn;
# ═══ Body size (для upload файлов в KB через API, если понадобится) ═══
client_max_body_size 50M;
client_body_timeout 300s;
# ═══ Rate limit ═══
limit_req zone=rag_api burst=10 nodelay;
limit_req_status 429;
# ═══ Edge auth check ═══
# Проверяем header X-Edge-Auth. Если не совпадает — 401.
# Значение подставится через $RAG_API_EDGE_BEARER (см. Шаг 7).
set $edge_bearer "REPLACE_ME_EDGE_BEARER";
if ($http_x_edge_auth != $edge_bearer) {
return 401 '{"error":"missing or invalid X-Edge-Auth"}\n';
}
# ═══ Health check (без auth — для мониторинга) ═══
location = /healthz {
access_log off;
return 200 '{"status":"ok"}\n';
add_header Content-Type application/json;
}
# ═══ Proxy на Open WebUI через VPN ═══
location / {
# Через AmneziaWG-туннель на ragserver
proxy_pass http://10.8.1.12:3000;
proxy_http_version 1.1;
# WebSocket upgrade (для streaming LLM ответов)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Стандартные proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# Пробрасываем Authorization как есть — Open WebUI проверит свой Bearer
proxy_set_header Authorization $http_authorization;
# НЕ пробрасываем X-Edge-Auth дальше (внутри не нужен)
proxy_set_header X-Edge-Auth "";
# Таймауты (LLM может генерить долго)
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Отключаем буферизацию — стриминг ответов
proxy_buffering off;
proxy_cache off;
}
}
Обратить внимание:
- REPLACE_ME_EDGE_BEARER — заменить на реальное значение из Vaultwarden OpenWebUI Edge Bearer · perplexity до nginx -t
- Rate limit 30 запросов/минуту с burst 10 — консервативно, потом подкрутим по факту
- WebSocket-заголовки — обязательны, иначе стриминг LLM отвалится
Шаг 7 — Активация nginx-конфига¶
# Подставить edge bearer
sudo nano /etc/nginx/sites-available/rag-api.alatyr-service.ru
# → заменить REPLACE_ME_EDGE_BEARER на реальное значение из Vaultwarden
# Symlink
sudo ln -s /etc/nginx/sites-available/rag-api.alatyr-service.ru \
/etc/nginx/sites-enabled/rag-api.alatyr-service.ru
# Проверка синтаксиса
sudo nginx -t
# Reload
sudo systemctl reload nginx
Если nginx -t ругается — читай ошибку, скорее всего опечатка в имени файла сертификата.
Шаг 8 — Проверка вручную с VPS¶
Проверка что endpoint жив (без auth):
Проверка что без Edge Bearer отбивает:
curl -s -w "\n%{http_code}\n" https://rag-api.alatyr-service.ru/api/config
# Ожидание: 401 {"error":"missing or invalid X-Edge-Auth"}
Проверка что с Edge Bearer но без OpenWebUI Bearer отбивает Open WebUI:
curl -s -w "\n%{http_code}\n" \
-H "X-Edge-Auth: <edge_bearer из Vaultwarden>" \
https://rag-api.alatyr-service.ru/api/config
# Ожидание: 401 от самого Open WebUI (или 200 если endpoint публичный — /api/config иногда открыт)
Проверка с обоими токенами:
curl -s \
-H "X-Edge-Auth: <edge_bearer>" \
-H "Authorization: Bearer <openwebui_token>" \
https://rag-api.alatyr-service.ru/api/v1/knowledge/ | head -50
# Ожидание: JSON со списком knowledge collections
Шаг 9 — Настройка custom-credential в Perplexity¶
В Perplexity Computer:
1. Открыть Settings → Connectors → Custom Credentials
2. + Add credential
3. Host: rag-api.alatyr-service.ru
4. Headers:
- Authorization: Bearer <sk-... из Vaultwarden запись OpenWebUI API · perplexity>
- X-Edge-Auth: <edge bearer из Vaultwarden>
5. Save
Тест из Perplexity (после сохранения credential):
Спросить в чате: "фетчни https://rag-api.alatyr-service.ru/api/v1/knowledge/ и покажи список коллекций"
Должен вернуть JSON со всеми knowledge collections твоего Open WebUI.
Шаг 10 — Мониторинг и алерты¶
Логи в VPS:
Полезные grep-фильтры:
# Все 401 (попытки без auth или с неправильным auth)
grep " 401 " /var/log/nginx/rag-api-access.log
# Все 429 (rate limit)
grep " 429 " /var/log/nginx/rag-api-access.log
# Медленные запросы (>10 сек)
awk '$NF > 10' /var/log/nginx/rag-api-access.log
fail2ban jail для rag-api (задача-follow-up):
Если словишь брутфорс — добавить в /etc/fail2ban/jail.d/rag-api.conf:
[nginx-rag-api-401]
enabled = true
port = https
filter = nginx-rag-api-401
logpath = /var/log/nginx/rag-api-access.log
maxretry = 5
findtime = 5m
bantime = 1h
Troubleshooting¶
502 Bad Gateway¶
- ragserver недоступен через VPN. Проверить:
ping 10.8.1.12с VPS. - На ragserver проверить:
sudo systemctl status awg-quick@awg-ragиsudo awg show awg-rag. - На VPS проверить peer:
sudo docker exec amnezia-awg2 awg show awg0и наличие10.8.1.12/32вallowed ips.
504 Gateway Timeout при генерации ответа LLM¶
- Ollama долго генерит. Увеличить
proxy_read_timeout 300s→600s. - Проверить что
proxy_buffering off— иначе стрим не работает
401 с валидным Bearer¶
- Проверить header не сломался: некоторые прокси едят
Authorization. В логах nginx смотреть переменную$http_authorization. - Проверить что Open WebUI видит header — временно
add_header X-Debug-Auth "$http_authorization" always;(потом убрать).
WebSocket disconnect при streaming¶
- Убедиться что есть
proxy_set_header UpgradeиConnection "upgrade"в location / proxy_http_version 1.1обязательно
rate limit срабатывает при обычной работе¶
- В логах:
limiting requests, excess: 10.500 by zone "rag_api" - Увеличить
rate=30r/m→rate=60r/mвlimit_req_zone
Что дальше¶
- Мониторинг Uptime Kuma (задача 28) — добавить monitor на
https://rag-api.alatyr-service.ru/healthz - fail2ban jail — если начнёт долбить кто-то извне
- ротация токенов — через 6 месяцев (по данным Vaultwarden
rotate_after) - аудит логов раз в неделю — сколько 401/429 накапало, кто пытался ломиться
Связанные файлы¶
- nginx-vps-configs.md — общий обзор nginx на VPS
- openwebui.md — сам Open WebUI и его API
- amneziawg-tunnels.md — VPN между VPS и ragserver
- certbot.md — TLS автообновление
- dns-alatyr-service.md — DNS-записи домена