The office-readiness audit found the foundation missing: configured API keys were decorative. Any bearer — or none — passed every model endpoint, per-key rate limits existed only in the form, and all real traffic was recorded as "anonymous", leaving the per-key spend table permanently blank. A middleware on the model surface (/v1/*, /route, /mcp) now enforces keys: - no keys configured → open mode as before (the console already warns); - keys exist → Bearer or x-api-key must match (401 invalid_api_key); - per-key rate_per_min → fixed one-minute window, 429 with Retry-After; - NEW per-key monthly_budget_usd → 429 insufficient_quota once the account's month-to-date spend crosses the ceiling; - the matched account rides the request into stats — the keys table's spend columns finally see real traffic. Month-to-date spend is tracked per account in stats and persisted in the totals sidecar with a watermark (no double counting across restarts). The key dialog gains a budget field; the table shows "budget $N/mo" in red once exhausted. All verified live: 401 wrong/missing key, 200 + attribution with the right key, 429 + Retry-After over the limit, 429 insufficient_quota on the second request past a $0.000001 budget. Also: the dashboard shows the OpenRouter credit balance (GET /api/v1/credits via the stored key, cached 5 min, hidden without a key, red under $5 — live: $16.18), the server drains connections on Ctrl-C instead of dropping them, request bodies are capped at 32 MB, and docs/DEPLOY(.ru) cover office deployment: systemd/launchd/Windows service, nginx/caddy TLS, security checklist, backups. 10 strings ×7 languages.
124 lines
4.6 KiB
Markdown
124 lines
4.6 KiB
Markdown
# Развёртывание cortiq-gateway для команды / офиса
|
||
|
||
Один шлюз на небольшом сервере, API-ключи по отделам, локальные модели для
|
||
дешёвых уровней, облачные — для сложных. Эта страница про эксплуатацию.
|
||
|
||
## 1. Запуск как сервис
|
||
|
||
### Linux (systemd)
|
||
|
||
```ini
|
||
# /etc/systemd/system/cortiq-gateway.service
|
||
[Unit]
|
||
Description=Cortiq Gateway
|
||
After=network-online.target
|
||
|
||
[Service]
|
||
User=cortiq
|
||
Environment=CORTIQ_GATEWAY_HOME=/var/lib/cortiq-gateway
|
||
ExecStart=/usr/local/bin/cortiq-gateway --no-port-search
|
||
Restart=always
|
||
RestartSec=3
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
```bash
|
||
sudo systemctl enable --now cortiq-gateway
|
||
```
|
||
|
||
### macOS (launchd)
|
||
|
||
```xml
|
||
<!-- ~/Library/LaunchAgents/com.cortiq.gateway.plist -->
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0"><dict>
|
||
<key>Label</key><string>com.cortiq.gateway</string>
|
||
<key>ProgramArguments</key><array>
|
||
<string>/usr/local/bin/cortiq-gateway</string>
|
||
<string>--no-port-search</string>
|
||
</array>
|
||
<key>RunAtLoad</key><true/>
|
||
<key>KeepAlive</key><true/>
|
||
</dict></plist>
|
||
```
|
||
|
||
```bash
|
||
launchctl load ~/Library/LaunchAgents/com.cortiq.gateway.plist
|
||
```
|
||
|
||
### Windows
|
||
|
||
Планировщик заданий → Создать задачу → «Выполнять вне зависимости от входа
|
||
пользователя», триггер «При запуске», действие `cortiq-gateway.exe
|
||
--no-port-search`. Либо [NSSM](https://nssm.cc):
|
||
`nssm install cortiq-gateway C:\bin\cortiq-gateway.exe`.
|
||
|
||
### Docker
|
||
|
||
```bash
|
||
docker run -d --restart unless-stopped -p 9000:9000 \
|
||
-v cortiq-data:/data ghcr.io/infosave2007/cortiq-gateway:latest
|
||
```
|
||
|
||
## 2. TLS и внешний мир
|
||
|
||
Сам шлюз говорит по HTTP — поставьте перед ним reverse-proxy с TLS, а шлюз
|
||
привяжите к localhost:
|
||
|
||
```toml
|
||
# gateway.toml
|
||
listen = "127.0.0.1:9000"
|
||
```
|
||
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name ai.example.com;
|
||
ssl_certificate /etc/letsencrypt/live/ai.example.com/fullchain.pem;
|
||
ssl_certificate_key /etc/letsencrypt/live/ai.example.com/privkey.pem;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:9000;
|
||
proxy_http_version 1.1;
|
||
proxy_buffering off; # SSE-стриминг
|
||
proxy_read_timeout 3600s; # долгие генерации
|
||
}
|
||
}
|
||
```
|
||
|
||
(`caddy reverse-proxy --from ai.example.com --to 127.0.0.1:9000` делает то же
|
||
с автоматическими сертификатами.)
|
||
|
||
## 3. Чек-лист безопасности
|
||
|
||
- **Токен админки**: задайте `[admin] token` (или `CORTIQ_ADMIN_TOKEN`) — без
|
||
него консоль предупреждает, что открыта сети.
|
||
- **API-ключи**: по ключу на отдел/разработчика на странице ключей. Как только
|
||
настроен хотя бы один ключ, модельные API требуют Bearer / x-api-key —
|
||
запросы без валидного ключа получают 401.
|
||
- **Rate-limit**: запросов/мин на ключ → 429 с Retry-After.
|
||
- **Месячные бюджеты**: потолок $ на ключ → 429 insufficient_quota, когда
|
||
траты аккаунта за месяц превысят лимит.
|
||
- Привязывайте к 127.0.0.1 за прокси; не выставляйте админку наружу голой.
|
||
|
||
## 4. Бэкапы
|
||
|
||
Всё живёт в data-директории (`CORTIQ_GATEWAY_HOME`):
|
||
|
||
- `gateway.toml` — полный конфиг вкл. ключи (или TOML-экспорт из консоли);
|
||
- `secrets.toml` — ключи провайдеров (chmod 600);
|
||
- `stats.jsonl` + `stats.jsonl.totals.json` — учёт запросов вкл. вечные
|
||
тоталы по ключам и месячные траты для бюджетов.
|
||
|
||
Скопируйте эти три файла — и инстанс поднимается где угодно.
|
||
|
||
## 5. Наблюдаемость
|
||
|
||
- Рабочий стол: KPI, расходы по ключам, последние запросы с коэффициентами
|
||
сложности, баланс OpenRouter (красный ниже $5).
|
||
- `GET /metrics` (включите `[telemetry] metrics = true`) для Prometheus.
|
||
- Просмотр логов в консоли; `stats.max_file_mb` ограничивает диск.
|