Содержание

Установка и запуск

Docker Compose, всё вместе

Compose с обоими сервисами лежит в репозитории гейта. Начинайте оттуда: в compose панели только панель и база.

git clone https://gitlab.zebrains.team/zebrains-gate/gate zbgate
cd zbgate
cp .env.example .env

# Оба секрета — сгенерировать, не придумывать
python -c "import os,base64;print('ZBGATE_SECRET_KEY=' + base64.b64encode(os.urandom(32)).decode())"
python -c "import secrets;print('ZBGATE_SESSION_SECRET=' + secrets.token_urlsafe(48))"

docker compose up -d --build

Гейт собирается на месте, панель тянется образом из реестра. Пока CI ничего не публиковал, соберите её один раз рядом:

git clone https://gitlab.zebrains.team/zebrains-gate/admin ../zbgate-admin
docker build -t zbgate-admin:local ../zbgate-admin
ADMIN_IMAGE=zbgate-admin:local docker compose up -d --build
  • Панель — http://localhost:8081, первый вход создаёт администратора
  • Гейт — http://localhost:8080

ZBGATE_SECRET_KEY должен совпадать у обоих сервисов байт в байт. Панель им шифрует секреты провайдеров, гейт расшифровывает. Разные значения дают нечитаемые секреты и ни одного внятного сообщения об ошибке.

Схему ставит панель при старте — она ей владеет. Гейт миграции не запускает никогда.

Первые шаги после запуска

  1. Открыть панель, создать администратора (первый вход).
  2. Завести провайдера: имя, тип, базовый адрес — Провайдеры.
  3. Добавить провайдеру ключ. Кнопка «Спросить /v1/models» проверит, что секрет рабочий, ещё до сохранения.
  4. Перечислить модели провайдера — или оставить пусто, если провайдер один.
  5. Задать цены — Цены. Без них токены считаются, а стоимость остаётся нулевой.
  6. Создать учётную запись человека с наценкой и лимитом ключей, выдать ему ключ — Пользователи и ключи.

Гейт подхватит всё без перезапуска.

curl localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer <ключ из панели>" \
  -H 'Content-Type: application/json' \
  -d '{"model":"anthropic/claude-opus-5","messages":[{"role":"user","content":"привет"}]}'

Бой

ZBGATE_TAG=1.4.0 docker compose -f docker-compose.prod.yml up -d

Оба сервиса образами из реестра, теги задаются явно — не latest, чтобы перезапуск ноды не выкатил новую версию сам по себе.

Что проверить перед первым боевым запуском:

  • ZBGATE_COOKIE_SECURE=true (по умолчанию так) и панель за HTTPS. На plain HTTP браузер не отдаст cookie и вход не сработает — это единственная причина выключать флаг.
  • ZBGATE_SECRET_KEY сохранён вне репозитория и вне образа. Потеря ключа означает перевод всех ключей провайдеров заново.
  • Порт гейта не выставлен в интернет без нужды: авторизация в нём есть, но каталог моделей и лимиты — не замена сетевому периметру.

Порядок выката при смене схемы

Панель владеет схемой, гейт её читает. Миграция, которая убирает или переименовывает колонку, ломает работающий гейт старой версии. Такие миграции помечены в шапке; выкатывайте панель и гейт вместе. Подробнее — Схема и миграции.

Без Docker

Порядок важен: схему ставит панель, поэтому она первая.

# Postgres
docker run -d --name zbgate-pg -e POSTGRES_USER=zbgate -e POSTGRES_PASSWORD=zbgate \
  -e POSTGRES_DB=zbgate -p 5432:5432 postgres:17-alpine

# Панель: зависимости, схема, сервер
cd zbgate-admin
uv venv --python 3.12 .venv && uv pip install -e '.[dev]' psycopg2-binary

# Логин и пароль базы — в PGUSER/PGPASSWORD, как для psql. Строка подключения по
# умолчанию их не содержит намеренно: пароль в исходниках — это тот пароль,
# который потом оказывается в бою.
export PGUSER=zbgate PGPASSWORD=zbgate
.venv/bin/alembic upgrade head
.venv/bin/uvicorn app.main:app --reload --port 8081

# Гейт, в другом терминале
cd ../zbgate
go build -o zbgate ./cmd/zbgate && ./zbgate -config config.yaml

Панели не нужен ни Node, ни сборка фронта: шаблоны Jinja, htmx одним файлом, CSS без препроцессора.

Проверка, что всё живо

curl -s localhost:8080/healthz   # гейт: генерация конфига, счётчики логов
curl -s localhost:8081/healthz   # панель

config_generation у гейта растёт при каждой успешной перезагрузке конфига — если правка в панели не подействовала, смотрите на этот счётчик первым. logs_dropped больше нуля означает, что очередь логов переполнялась.