SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-28

Як self-host Open Connector для AI-агентів

Запустіть Open Connector на власному VPS: pinned image, TLS origin, OAuth callbacks і резервні копії, щоб AI-агенти не зберігали SaaS-токени.

Що Open Connector робить для AI-агента

Self-hosting Open Connector розміщує один auth gateway між вашими AI-агентами та всіма API програмних сервісів (SaaS), які вони викликають. Тому агент ніколи не зберігає токен провайдера. Це open source gateway від OOMOL Lab за ліцензією Apache 2.0. Він працює як один контейнер, зберігає стан в одному файлі SQLite і надає дії провайдерів через HTTP та MCP (model context protocol).

Проблеми починаються вже з другої інтеграції. Кожен провайдер має власний OAuth (open authorization) flow, власний строк дії refresh token і власні назви scope. Щоб вручну підключити до агента п’ять провайдерів, потрібно реалізувати п’ять redirect handlers, п’ять сховищ облікових даних і п’ять refresh loops, які мають запускатися до завершення строку дії токена. Майже ніхто не пише такий код. Натомість для кожного сервісу створюють один довгоживучий personal access token і вставляють його в конфігурацію агента, файл середовища або сам prompt. Тоді цей токен може читати кожен tool, який запускає агент, і він потрапляє до transcript. Саме цю проблему описує матеріал як не зберігати секрети в AI-агентах.

Auth gateway розділяє credential на дві частини. Gateway зберігає credential провайдера й виконує OAuth flow. Агент отримує runtime token, дійсний лише для gateway. Коли агент викликає дію, gateway завантажує збережений credential, додає його до вихідного запиту на стороні сервера та повертає лише тіло відповіді. Агент ніколи не отримує access token провайдера. Тому витік transcript агента призводить до втрати одного відкличного runtime token, а не облікового запису GitHub.

У каталозі заявлено понад 1,000 провайдерів і 10,000 готових дій. Це власні показники проєкту, які неможливо перевірити ззовні. Натомість можна перевірити саму структуру: один HTTP endpoint для кожної дії, одне збережене connection для кожного провайдера і один token для кожного агента. Якщо частина про агента для вас ще нова, а такі терміни, як tool call або MCP server, ще не стали звичними, поетапний матеріал як вивчити AI-агентів з нуля послідовно пояснює loop, tools і практики безпеки, які такий gateway передбачає.

Навіщо self-host Open Connector замість використання розміщеного сервісу конекторів

Розміщений сервіс конекторів виконує ту саму роботу та зберігає refresh tokens для кожного підключеного до нього провайдера. Refresh token для Google або GitHub — це довготривалий криптографічний ключ до вашої пошти та репозиторіїв, який зазвичай залишається чинним навіть після зміни пароля. Якщо їхню систему буде зламано, буде скомпрометовано і ваші облікові дані. Self-hosting переносить ці записи до SQLite на машині, яку ви орендуєте й адмініструєте, та захищає їх ключем, який ніколи не залишає ваш сервер.

Перш ніж починати, оцініть вартість цього рішення. Цей VPS стане найціннішим сервером у вашій інфраструктурі. В одному файлі він зберігатиме робочі облікові дані для десятка сервісів, тому поводитися з ним потрібно як із хостом для password manager: firewall має відкривати лише 443, спільні облікові записи не використовуйте, резервну копію потрібно хоча б один раз фактично відновити, а коли сервер перестає відповідати, має надходити сповіщення. Якщо ви не розмістили б на цьому сервері своє сховище паролів, не розміщуйте на ньому й конектор.

Закріпіть версію перед установленням

Open Connector — молодий проєкт. Репозиторій уперше з’явився 29 June 2026, а станом на 1 August 2026 найновішим тегованим релізом є v1.3.3, опублікований 30 July 2026 і також позначений тегом latest. Registry також публікує тег tip, зібраний із найновішого коміту в main.

У такому новому проєкті рухомі теги часто змінюються. docker compose pull, який перескакує через два релізи, може змінити endpoint, від якого залежить ваш агент, і ви витратите вечір на налагодження проблеми, помилково вважаючи її проблемою агента. Закріпіть image за тегом релізу та оновлюйте його тоді, коли вирішите самі, попередньо ознайомившись із нотатками до релізу.

Розгортання Open Connector за TLS на власному VPS

Перш ніж запускати контейнер, вам потрібні:

  • Docker із Compose plugin на Ubuntu 24.04 або близькій версії
  • hostname, A-запис якого вказує на цей VPS, наприклад connect.example.com
  • reverse proxy, який уже завершує TLS (transport layer security) для цього hostname
  • два випадкові секрети, які буде згенеровано нижче

У матеріалі Traefik reverse proxy для кількох застосунків Docker Compose описано налаштування проксі. Повний процес налаштування сертифікатів для одного застосунку наведено в посібнику n8n на VPS із Docker і HTTPS.

Спочатку згенеруйте секрети. Ключ шифрування захищає збережені облікові дані. Токен адміністратора захищає вебконсоль і всю поверхню /api. Для жодного з них немає значення за замовчуванням, а runtime без них запускається без помилок.

mkdir -p ~/open-connector && cd ~/open-connector
umask 077
printf 'OOMOL_CONNECT_ENCRYPTION_KEY=%s\n' "$(openssl rand -base64 32)" > .env
printf 'OOMOL_CONNECT_ADMIN_TOKEN=%s\n' "$(openssl rand -base64 32)" >> .env
chmod 600 .env

Скопіюйте обидва значення у password manager зараз, до першого запуску. Ключ шифрування неможливо відновити, і причину наведено нижче в переліку помилок.

Тепер compose.yaml. Він відрізняється від upstream-прикладу у двох місцях, і обидві зміни важливі.

services:
  connector:
    image: ghcr.io/oomol-lab/open-connector:v1.3.3
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - connector-data:/app/data
    environment:
      OOMOL_CONNECT_DATA_DIR: /app/data
      OOMOL_CONNECT_ORIGIN: "https://connect.example.com"
      OOMOL_CONNECT_ENCRYPTION_KEY: "${OOMOL_CONNECT_ENCRYPTION_KEY:?set this in .env}"
      OOMOL_CONNECT_ADMIN_TOKEN: "${OOMOL_CONNECT_ADMIN_TOKEN:?set this in .env}"

volumes:
  connector-data:

Перша зміна — зафіксований tag замість latest. Друга — порт. Upstream-файл публікує 3000:3000, який прив’язує порт до всіх інтерфейсів host. Docker записує опубліковані порти до таблиці NAT (network address translation) ще до того, як пакет потрапить у ланцюжок фільтрації ufw. Тому ufw deny 3000 не закриває цей порт. Це описано в матеріалі чому порти Docker обходять ufw. Запис 127.0.0.1:3000:3000 публікує порт лише на loopback interface, а ваш reverse proxy підключається з того самого host.

:? позначає кожну змінну як обов’язкову. Тому stack відмовляється запускатися, якщо .env відсутня, замість запуску з незашифрованими обліковими даними. Зберігання значень у .env, а не у compose-файлі — це підхід із матеріалу env-файли та секрети Docker Compose.

docker compose up -d
docker compose logs -n 30 connector
curl -s http://127.0.0.1:3000/health
sudo ss -tlnp | grep 3000

/health повертає { "ok": true } після запуску runtime. ss має вивести 127.0.0.1:3000. Рядок 0.0.0.0:3000 означає, що зіставлення порту все ще відповідає upstream-варіанту, а gateway напряму відповідає всьому інтернету. Connection refused під час health check означає, що контейнер ще не слухає порт. Перед змінами в проксі прочитайте журнали.

Мітки Traefik для цього самого сервісу
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.connector.rule=Host(`connect.example.com`)"
      - "traefik.http.routers.connector.entrypoints=websecure"
      - "traefik.http.routers.connector.tls.certresolver=le"
      - "traefik.http.services.connector.loadbalancer.server.port=3000"

Коли Traefik працює в Docker на тому самому host, підключіть цей сервіс до мережі Traefik і видаліть блок ports:. Traefik підключається до контейнера через внутрішню мережу, тому публікувати порт на host взагалі не потрібно. certresolver=le має збігатися з назвою resolver у статичній конфігурації Traefik. Інакше router запуститься без сертифіката.

Чому OAuth вимагає справжнє ім’я хоста

OOMOL_CONNECT_ORIGIN — це параметр, який часто пропускають. Через це OAuth не працює, а помилка виглядає як проблема на боці провайдера. Runtime формує redirect URI на основі цього origin у форматі <origin>/oauth/callback. Якщо параметр не задано, origin за замовчуванням має значення http://localhost:3000. Тому runtime надсилає провайдеру redirect URI http://localhost:3000/oauth/callback, тоді як у вашому OAuth-застосунку зареєстровано https://connect.example.com/oauth/callback. Ці два рядки відрізняються, тому GitHub відповідає:

The redirect_uri MUST match the registered callback URL for this application.

OAuth-провайдер перенаправляє браузер назад на цей URI. Отже, це має бути адреса, доступна із зовнішньої мережі. Провайдери відхиляють звичайний http:// для всіх адрес, крім localhost. Саме тому для цього розгортання потрібні hostname і сертифікат. Задайте origin до першого запуску, оскільки значення зчитується під час запуску: після редагування .env або compose.yaml знову виконайте docker compose up -d, щоб застосувати зміни.

Підключіть першого провайдера через OAuth

Спочатку створіть OAuth app у провайдера. У GitHub відкрийте Settings, потім Developer settings, OAuth Apps і New OAuth App. Укажіть URL callback для авторизації: https://connect.example.com/oauth/callback. Збережіть client ID і client secret.

Кожен виклик /api передає admin token, тому експортуйте його один раз для поточної shell-сесії.

export ADMIN_TOKEN='paste-the-admin-token'
curl -s https://connect.example.com/api/oauth/configs \
  -H "authorization: Bearer $ADMIN_TOKEN"

У цьому списку показано redirect URI, який runtime очікує для кожного провайдера. Це найшвидший спосіб перевірити, що origin застосовано. Якщо там і далі вказано localhost, контейнер працює зі старим значенням, і OAuth flow завершиться помилкою на останньому кроці.

Збережіть client credentials і запустіть авторизацію.

curl -s -X PUT https://connect.example.com/api/oauth/configs/github \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"clientId":"...","clientSecret":"..."}'

curl -s -X POST https://connect.example.com/api/oauth/authorizations \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"service":"github"}'

Другий виклик повертає authorizationUrl. Відкрийте його в браузері, підтвердьте scopes, і провайдер поверне браузер на /oauth/callback. Там runtime обмінює code і зберігає credential. Вебконсоль за вашим origin виконує ті самі кроки через форму, використовуючи той самий admin token. Провайдери, які використовують звичайний API key, пропускають усі ці кроки: PUT /api/connections/<service> із {"authType":"api_key","values":{"apiKey":"..."}} безпосередньо зберігає key.

Надавайте кожному агенту runtime token, а не credential

Агент автентифікується на gateway за допомогою runtime token, який випускає admin API.

curl -s -X POST https://connect.example.com/api/runtime-tokens \
  -H "authorization: Bearer $ADMIN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"name":"research-agent"}'

У відповіді міститься token, що починається з oct_. Випускайте окремий token для кожного агента та називайте його за іменем цього агента. Якщо token неможливо ідентифікувати, його відкликання означатиме відкликання всіх token. Після цього агент викликає actions через звичайний HTTP.

curl -s -X POST https://connect.example.com/v1/actions/github.get_current_user \
  -H "authorization: Bearer oct_..." \
  -H 'content-type: application/json' \
  -d '{"input":{}}'

Коректна відповідь — це envelope, у якому поле success має значення true, а payload провайдера міститься в data. GitHub token ніде в цій відповіді не міститься. Для MCP client вкажіть https://connect.example.com/mcp і використайте той самий bearer header. Gateway надає discovery tools, зокрема search_actions і execute_action, замість окремого tool для кожного API. Це зменшує кількість tools у списку агента. У розділі Запуск MCP servers на VPS описано налаштування клієнтської частини.

Перед завершенням виконайте ще одну перевірку. Повторіть виклик action, видаливши header authorization. У власному quickstart проєкту викликається /v1 без bearer, тому інсталяція без налаштованої runtime authentication виконуватиме actions для будь-кого, хто має доступ до порту. Якщо виклик без автентифікації успішний, є два варіанти: налаштувати runtime tokens і переконатися, що анонімний виклик тепер завершується помилкою, або обмежити /api, /v1 і /mcp на reverse proxy адресами, з яких підключаються ваші агенти. Лише /oauth/callback має залишатися відкритим для всіх, оскільки це єдиний шлях, потрібний для browser redirect від провайдера.

Скоротіть список дій до необхідного агенту

Шлюз із тисячею провайдерів за ним має велику поверхню атаки для мовної моделі. Вона стає ще більшою, коли модель починає читати текст, створений не нею, оскільки сторінка, яку повертає ваш власний екземпляр SearXNG у відповідь на вебпошук агента, може містити інструкції, спрямовані на будь-які дії, доступні агенту. Те саме обмеження, завдяки якому агент для роботи з кодом вносить найменшу працездатну зміну, має поширюватися і на його дозволи: надавайте лише кілька дій, фактично потрібних для завдання, і нічого більше. Є два елементи керування, які звужують цей набір.

OOMOL_CONNECT_ALLOWED_ACTIONS приймає список дозволених дій через кому та підтримує service.* і *. OOMOL_CONNECT_BLOCKED_ACTIONS — це список заборонених дій, і він має пріоритет. Якщо задати для списку дозволених дій значення github.get_current_user,github.list_issues, усі інші дії буде відхилено незалежно від запиту агента. Саме це відрізняє помилку від інциденту. Токени середовища виконання мають власні правила дій на додаток до глобальних правил, а їхній список allowedProxies за замовчуванням порожній. Тому POST /v1/proxy/:service буде відхилено, доки ви явно не надасте цей дозвіл. Ця кінцева точка проксі пересилає необроблений запит провайдеру разом із вашими обліковими даними, тому залишайте її порожньою, якщо вона не потрібна конкретному агенту.

OOMOL_CONNECT_ALLOW_PRIVATE_NETWORK за замовчуванням має значення false. Це не дає підключенню до self-hosted провайдера звертатися до приватної адреси, наприклад до сервісу метаданих хмари на 169.254.169.254 або до вашої бази даних у тій самій мережі. Залишайте цей параметр вимкненим. Умикайте його лише для провайдера, який ви розгорнули самостійно.

Резервне копіювання сервера, на якому зберігаються всі токени

Важливі дві речі, і кожна з них без іншої непридатна. База даних у /app/data/connect.sqlite всередині тому connector-data містить зашифровані облікові дані. Ключ шифрування у .env розшифровує їх. Резервна копія тому без ключа нічого не відновить, а ключ без тому також нічого не відновить, тому ключ зберігайте у password manager, а том додайте до звичайної ротації резервних копій.

Зупиніть контейнер, поки копіюєте файл SQLite, оскільки копія, створена під час запису, може відновитися як пошкоджена база даних.

docker volume ls | grep connector-data
docker compose stop connector
docker run --rm -v open-connector_connector-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/connector-data.tgz -C /data .
docker compose start connector

Назва тому — це назва каталогу проєкту плюс _connector-data, тому потрібна перша команда: вставте фактичну назву в третю команду. Передайте архів із VPS за допомогою резервного копіювання restic із VPS. restic шифрує його перед передаванням, оскільки цей архів містить сховище облікових даних.

Середовище виконання зберігає останні запуски дій як записи аудиту — за замовчуванням 5,000 записів. Тому в консолі можна побачити, який агент, що саме і коли запускав. Цей журнал слід читати насамперед, коли агент поводиться нетипово. Також налаштуйте сторінку стану Uptime Kuma для перевірки https://connect.example.com/health. Коли gateway перестає відповідати, агенти починають поводитися непередбачувано, а інформація про недоступність gateway заощаджує годину читання виводу агентів.

Що може зламатися і яке повідомлення ви побачите

redirect_uri_mismatch у провайдера. Джерело та зареєстрована URL-адреса callback відрізняються. Порівняйте точний рядок із /api/oauth/configs з налаштуваннями застосунку у провайдера, зокрема https зі http, а також перевірте кінцевий слеш.

Кожен виклик /api повертає 401. Заголовок із токеном адміністратора відсутній або має неправильну назву. Заголовок має вигляд Authorization: Bearer <token>, а вебконсоль запитує той самий токен.

Контейнер працює, а облікові дані зберігаються у відкритому вигляді. Це відбувається, коли OOMOL_CONNECT_ENCRYPTION_KEY не потрапляє до контейнера, оскільки runtime зберігає записи облікових даних у незашифрованому вигляді, замість того щоб відмовитися від запуску. Перевірте це у власній інсталяції: підключіть провайдера за допомогою API key, який можна розпізнати, а потім виконайте пошук цього ключа в базі даних.

docker compose cp connector:/app/data/connect.sqlite /tmp/connect.sqlite
grep -c 'github_pat_' /tmp/connect.sqlite
shred -u /tmp/connect.sqlite

Значення понад 0 означає, що ключ не застосовано. Перевірте, що .env розташований в одному каталозі з compose.yaml і що docker compose config виводить це значення. Якщо ключ налаштовано, той самий пошук поверне 0, оскільки запис захищено за допомогою AES-256-GCM (advanced encryption standard, 256-bit key, Galois/counter mode).

Після відновлення нічого не розшифровується. Ключ шифрування змінився або був втрачений. За задумом його ніколи не записують поруч із даними, тому шляху відновлення немає, і звернення до служби підтримки не допоможе. Підключіть усіх провайдерів повторно. Ротація підтримується через окрему змінну ключа та команду роботи з даними в runtime, тому перед ротацією ознайомтеся з нотатками до поточного релізу.

Agent повертає помилку із зазначенням action, який він бачить у каталозі. Виявлення та виконання — окремі процеси. Action може відображатися в search_actions, але його все одно може відхилити OOMOL_CONNECT_ALLOWED_ACTIONS, denylist або власні правила цього runtime token.

Оновлення. Створіть резервну копію тому, змініть image tag на новий реліз, а потім docker compose pull && docker compose up -d. Моніторте docker compose logs -n 50 connector на наявність повідомлення про міграцію, після чого повторно виконайте перевірку працездатності та одну реальну action, перш ніж знову довірити системі роботу. Відкат означає повернення старого tag. Це працює лише тому, що tag було зафіксовано.

FAQ

Чи потрібен публічний домен для self-hosting Open Connector?

Для провайдерів, які використовують API key, — ні: достатньо gateway на 127.0.0.1. Для OAuth на практиці — так. Провайдер перенаправляє браузер на ваш callback URL, тому ця URL-адреса має бути доступною з публічного інтернету, а провайдери відхиляють звичайний http:// поза localhost. Перед першим запуском задайте OOMOL_CONNECT_ORIGIN як ім’я хоста https:// і зареєструйте <origin>/oauth/callback в OAuth-застосунку провайдера.

Що станеться, якщо я втрачу ключ шифрування Open Connector?

Збережені облікові дані неможливо розшифрувати, і відновлення не передбачене. Ключ навмисно не зберігається разом із даними, тому ніхто, хто має доступ до бази даних, не може їх прочитати, зокрема й ви. Єдиний варіант — задати новий ключ і повторно підключити кожного провайдера. Зберігайте ключ у password manager, а базу даних включіть до циклу резервного копіювання, оскільки для відновлення потрібні обидва компоненти.

Чи може мій AI agent побачити access token провайдера?

Ні, якщо він викликає провайдера через gateway. Agent автентифікується за допомогою runtime token, що починається з oct_, а gateway додає облікові дані провайдера до вихідного запиту на сервері та повертає лише відповідь. Цю властивість порушують дві речі: endpoint /v1/proxy/:service, який пересилає необроблені запити з доданими обліковими даними та навмисно має порожні grants, і вставлення API key безпосередньо в agent, що повністю обходить gateway.

Чи має gateway бути доступним із публічного інтернету?

Доступним має бути лише /oauth/callback. Опублікуйте порт контейнера на 127.0.0.1, щоб правила Docker NAT не змогли відкрити до нього доступ поза вашим firewall, і розмістіть reverse proxy перед ним. Потім перевірте один виклик action без заголовка authorization. Якщо він успішний, обмежте /api, /v1 і /mcp на proxy адресами, з яких працюють ваші agents, доки доступними не залишаться лише автентифіковані виклики.

Чи готовий Open Connector до використання в production?

Проєкт ліцензовано за Apache 2.0 і швидко розвивається: repository з’явився 29 June 2026, а v1.3.3 було випущено 30 July 2026, тому сприймайте кожен номер версії в цьому посібнику як знімок стану на 1 August 2026. Запускайте його із зафіксованим release tag, ніколи не використовуйте latest або tip, перед кожним оновленням читайте release notes і зберігайте резервну копію volume, яку ви вже одного разу відновили. Архітектура добре підходить для сервера під вашим контролем; ризик пов’язаний зі швидкою зміною версій, а не з архітектурою.