Содержание

Пользователи и ключи

Политика живёт на человеке, ключи её наследуют. Это главное, что стоит понять про эту страницу.

Страница /keys устроена по людям, а не по ключам: карточка на человека, внутри — его ключи. Так и организовано то, о чём эта страница: наценка и потолок числа ключей задаются один раз на человека, и плоский список ключей делал владельца колонкой, а вопрос «сколько ему можно» оставлял на другой странице. Ключи без владельца — служебные — идут отдельным разделом в конце: наследовать наценку им не от кого.

/settings остаётся про то, кто может войти: создание записей, роли, пароли, каталог. Наценка, потолок, метки и сами ключи — здесь.

Учётная запись

Поле Смысл
Адрес и имя вход и подпись в интерфейсе
Роль admin видит панель; user — только свои ключи и расход
Множитель стоимости наценка на все его ключи
Ключей не больше потолок; пусто — общий лимит, ноль — ни одного
Всего запросов в минуту на все ключи вместе
Всего токенов в минуту на все ключи вместе
Общий бюджет на все ключи вместе, с периодическим сбросом
Метки визуальные ярлыки, гейт их не читает

Источник учётной записи — локальный пароль (Argon2id) или каталог (см. Вход через каталог).

Последнего администратора нельзя ни разжаловать, ни удалить: иначе панель осталась бы без входа, а путь восстановления — ручной INSERT.

Множитель стоимости

Наценка поверх цены модели. Её применяет гейт при расчёте запроса, поэтому она попадает и в журнал, и в накопленный расход, и в бюджет, который с ним сравнивается — все три говорят в одних единицах.

Одна на человека, а не на ключ. Иначе у одного человека с ключом на ноутбуке и ключом в CI были бы две разные цены, и счёт зависел бы от того, каким credential сделан запрос. Ключ переспорить наценку не может.

Служебный ключ без владельца тарифицируется по себестоимости: наследовать не от кого.

Ноль запрещён. Ключ, который никогда не накапливает расход, выглядит точно как отсутствующая тарифная карта, а «бесплатно» в этой панели уже выражается ценой ноль.

Владельцу наценка не показывается. Он видит уже умноженную цену — и в валюте отображения, если она задана.

Ключи

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

Поле Смысл
Название видно в журнале
Владелец откуда берётся наценка; пусто — служебный ключ
Запросов в минуту (rpm) пусто — без ограничения, ноль — запретить всё
Токенов в минуту (tpm) то же
Бюджет в долларах; при исчерпании 402
Сброс бюджета ежедневно / еженедельно / ежемесячно
Действует до срок действия
Разрешённые провайдеры пусто — все
Разрешённые модели пусто — все

Пусто и ноль — намеренно разные значения: пусто снимает потолок, ноль ставит его в «ничего нельзя».

Сырое значение ключа показывается один раз, при создании. В базе только SHA-256: утёкшая база не воспроизводится против гейта. Потерянный ключ не восстанавливается — только перевыпускается, и старое значение перестаёт работать в тот же момент.

Почему SHA-256, а не медленный хеш: это 256 бит машинной энтропии, а не человеческий пароль — низкоэнтропийного пространства перебора, от которого защищал бы KDF, здесь нет. А проверять приходится на каждом запросе.

Ограничения доступа

Проверяются по уже разрешённой цели, поэтому работают и с префиксом, и без. Правило можно написать любым из двух способов — gpt-4o или openai/gpt-4o — и обойти его, написав запрос иначе, не получится.

Регистр не важен, см. Адресация моделей.

У ключа два списка моделей, и они складываются, а не заменяют друг друга:

Список Кто задаёт Где
allowed_models администратор /keys
owner_allowed_models владелец ключа «Мои ключи»

Действующий набор — пересечение, где пустое с любой стороны означает «без ограничения». Владелец сужает выданное, но не расширяет: модель, которой ему не выдали, отклоняется с указанием, какая именно.

Два списка, а не один, по конкретной причине. Если бы владелец писал в тот же столбец, то, сузив ключ до одной модели, он потерял бы разрешение на остальные: его собственное сужение стало бы потолком, и вернуть модель, которую администратор разрешил, было бы нечем. С rpm, tpm и бюджетом этого не возникает — там потолок задан политикой самообслуживания, а на ключе лежит действующее значение.

Общие лимиты человека

Лимиты на ключе отвечают на вопрос «сколько можно этой машине». Вопрос «сколько можно этому человеку» они не закрывают: ключ на ноутбуке, ключ в CI и ключ в скрипте по отдельности вписываются в свои бюджеты, а вместе тратят втрое. Поэтому у учётной записи есть свои rpm, tpm и бюджет, считаемые по сумме всех её ключей.

Оба уровня действуют одновременно. Запрос проходит, если проходит и по ключу, и по человеку. Отказ называет сработавший уровень:

API key "ноутбук" exceeded its request-per-minute limit
account "Разработчик" exceeded its request-per-minute limit across all of its keys
account "Разработчик" has reached its total spending limit across all of its keys

Без этого «429» пришлось бы разбирать вслепую — на ключе-то лимит ещё есть.

Накопленный расход человека лежит отдельным столбцом на учётной записи, а не считается суммой по ключам на каждом запросе: гейт и так читает эту строку одним JOIN при проверке ключа, и наращивает оба счётчика одним оператором — двумя осталось бы окно, в котором ключ списан, а человек ещё нет, и это ровно то окно, через которое лимит и проскакивает.

Общий бюджет сбрасывается по своему расписанию: человек на месячном лимите может держать ключи со недельным сбросом. Обнулить его вручную можно кнопкой в его настройках — расход по отдельным ключам при этом остаётся, это разные потолки.

Служебный ключ без владельца ничего этого не наследует: наследовать не от кого.

Владелец видит свои общие лимиты на «Моих ключах» — иначе отказ «исчерпан общий бюджет» был бы необъясним. Опустить их со своей стороны он не может: это потолок администратора на него целиком, а ограничивать он может отдельные ключи.

Лимит rpm и tpm

rpm проверяется до запроса. tpm — по уже потраченному за минуту, поэтому один запрос может его превысить: стоимость запроса неизвестна до его завершения, а отказывать начинать работу, цена которой ещё не знаема, — хуже.

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

Самообслуживание

Если включено в настройках, человек без прав администратора выпускает ключи сам, на странице «Мои ключи».

Ключ выпускается с потолками из политики самообслуживания: rpm, tpm, бюджет и период сброса. Лимиты берутся из политики, а не из формы — ключ, который человек выдаёт себе, не должен быть ключом, размер которого он же и выбирает.

Владелец может опустить лимиты своего ключа ниже выданных, но не поднять. Попытка поднять отклоняется, а не подрезается молча: тихо записать другое число, чем набрано, — так человек и приходит к уверенности в лимите, которого у него нет. Пустое поле означает «сколько разрешено», а не «без ограничения».

Владелец может и удалить свой ключ: держать несколько имеет смысл только если один можно отдать. История запросов при этом остаётся — она денормализована.

Сужение по моделям тоже в его руках: рядом с лимитами есть список моделей с чипами того, что ему выдано (а если не выдано ничего конкретного — того, что доступно через его провайдеров). Очистка поля возвращает всё выданное: потолок администратора никуда не девался. Каталог /v1/models сужается вместе с этим — клиент видит ровно то, чем ключ может пользоваться.

Потолок числа ключей один и тот же, выпускаете ли вы ключ себе или его выдаёт администратор: обойти его через чужие руки нельзя. Администратору, которому нужен ещё один, следует поднять потолок человеку — так остаётся один ответ на вопрос «сколько ему можно».

Что видит не-администратор

Страница «Мои ключи»: свои ключи и их лимиты, расход за 30 дней, разбивка по моделям и по дням, последние запросы со временем и скоростью, цены доступных моделей — уже с наценкой и в валюте отображения. Плюс возможность опустить лимиты и сузить ключ по моделям.

Ни наценки, ни чужих ключей, ни провайдеров, ни журнала целиком. Каждый запрос этой страницы ограничен ключами, которыми владеет вошедший.