SSD Nodes Learn 🎉 VPS від $5.50/міс
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-13

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

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

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

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

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

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

Каталог заявляє про понад 1,000 провайдерів і 10,000 готових дій. Це власні показники проєкту, які неможливо перевірити ззовні. Натомість можна перевірити саму структуру: один HTTP endpoint для кожної дії, одне збережене підключення для кожного провайдера і один токен для кожного агента.

Навіщо розгортати Open Connector самостійно, а не використовувати керований сервіс конектора

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

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

Зафіксуйте версію до встановлення будь-чого

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

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

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

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

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

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

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

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

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

Тепер 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:

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

:? позначає кожну змінну як обов’язкову. Тому стек відмовляється запускатися, якщо .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 } після запуску середовища виконання. ss має вивести 127.0.0.1:3000. Рядок 0.0.0.0:3000 означає, що зіставлення портів усе ще залишилося upstream-варіантом, а gateway безпосередньо відповідає всьому інтернету. Connection refused під час перевірки стану означає, що контейнер ще не прослуховує порт. Спочатку перегляньте журнали, а вже потім змінюйте налаштування проксі.

Мітки 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 на тому самому хості, підключіть цей сервіс до мережі Traefik і видаліть блок ports:, оскільки Traefik отримує доступ до контейнера через внутрішню мережу, а публікувати порт на хості взагалі не потрібно. certresolver=le має збігатися з назвою resolver у статичній конфігурації Traefik, інакше router запуститься без сертифіката.

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

OOMOL_CONNECT_ORIGIN — це параметр, який часто пропускають. Через це OAuth не працює, а помилка схожа на несправність провайдера. Середовище виконання формує URI перенаправлення на основі цього origin у форматі <origin>/oauth/callback. Якщо значення не задано, origin за замовчуванням дорівнює http://localhost:3000. Тому середовище виконання надсилає провайдеру 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. Саме тому для цього розгортання потрібні ім’я хоста та сертифікат. Задайте origin до першого запуску, оскільки значення зчитується під час запуску. Після редагування .env або compose.yaml знову виконайте docker compose up -d, щоб застосувати зміни.

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

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

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

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

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

Збережіть облікові дані клієнта, а потім почніть авторизацію.

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 обміняє код і збереже облікові дані. Вебконсоль за вашим origin виконує ті самі кроки через форму, використовуючи той самий токен адміністратора. Провайдери, які використовують звичайний API key, пропускають цей процес: PUT /api/connections/<service> з {"authType":"api_key","values":{"apiKey":"..."}} безпосередньо зберігає ключ.

Надайте кожному агенту 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-клієнта вкажіть https://connect.example.com/mcp і використайте той самий bearer header. Gateway надає discovery tools, зокрема search_actions і execute_action, замість окремого tool для кожного API. Це зменшує список tools агента. У розділі Запуск MCP-серверів на VPS описано налаштування клієнтської частини.

Перш ніж вважати налаштування завершеним, виконайте ще одну перевірку. Повторіть виклик action, видаливши header authorization. У власному quickstart проєкту /v1 викликається взагалі без bearer. Тому інсталяція без налаштованої runtime auth виконуватиме 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 розшифровує їх. Резервна копія тому без ключа нічого не відновить, а ключ без тому також нічого не відновить. Тому ключ потрібно зберігати у вашому менеджері паролів, а том — у звичайному циклі резервного копіювання.

Зупиніть контейнер, поки копіюєте файл 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. Цей інструмент шифрує архів перед передаванням, оскільки в ньому зберігається сховище облікових даних.

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

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

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

Кожен виклик /api повертає 401. Заголовок з admin token відсутній або містить помилку. Заголовок має вигляд Authorization: Bearer <token>, і web console запитує той самий 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, тому перед ротацією ознайомтеся з поточними release notes.

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

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

FAQ

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

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

Що станеться, якщо я втрачу encryption key 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, щоб правила NAT Docker не змогли відкрити його за межами вашого firewall, і розмістіть reverse proxy перед ним. Потім виконайте один action call без заголовка authorization. Якщо виклик успішний, обмежте /api, /v1 і /mcp на proxy адресами, якими користуються ваші agents, доки працюватимуть лише автентифіковані виклики.

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

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