# Восстановление доступа к серверу

Сервер остаётся привязан к текущему управляющему ключу, поэтому после истечения или отзыва этого ключа другой ключ сервер не видит. Доступ возвращается сменой управляющего ключа в личном кабинете, сервер при этом не пересоздаётся.

## Как выглядит потеря доступа

Потеря доступа проявляется так:

- `GET /v1/infra/servers` возвращает успех и пустой массив `data`, хотя серверы на портале есть.
- `GET /v1/infra/servers/:id` по известному идентификатору возвращает `404 NOT_FOUND`.
- `GET /v1/me` при этом подтверждает, что новый ключ действует: `accessMode` равен `READWRITE`, скоуп `vibe:infra` присутствует.
- В личном кабинете сервер виден в разделе «Серверы Black Hole» и продолжает работать.

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

```json
{
  "success": true,
  "data": []
}
```

Обращение к конкретному серверу тем же ключом:

```json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Server not found"
  }
}
```

Надёжная проверка — сравнить личный кабинет и API: сервер виден в разделе «Серверы Black Hole», но не приходит в `GET /v1/infra/servers`. Кабинет показывает серверы по владельцу, поэтому он и есть источник правды о том, что сервер существует.

Счётчики в `GET /v1/me` дают дополнительную подсказку, но не доказательство. Поле `infra.limits.used` считает отдельные виртуальные машины (`STANDALONE`) текущего ключа, а `infra.limits.breakdown` — серверы владельца по всем его ключам на портале. Нулевой `used` при ненулевом `breakdown.total` сам по себе не говорит, что текущий ключ не управляет ничем: `used` не считает галактики и Galaxy-приложения даже тогда, когда управляет ими именно он. Это повод сверить с кабинетом, не более:

```json
{
  "success": true,
  "data": {
    "infra": {
      "limits": {
        "max": 3,
        "used": 0,
        "breakdown": {
          "direct": 7,
          "agents": 0,
          "bots": 0,
          "total": 7
        }
      }
    }
  }
}
```

У подсказки есть и другие границы. Сервер, полностью потерявший ключ, в `breakdown` не попадает — там оба счётчика останутся нулевыми, хотя сервер работает и виден в кабинете. И наоборот, `breakdown` считает в том числе виртуальные машины агентов и ботов: они тоже дают `used: 0` при ненулевом `total`, но управляют ими в их разделах личного кабинета, а не через `/v1` — см. [AI-агенты](/docs/agents). Про галактики подробнее — [Galaxy-приложение](/docs/infra/galaxy). Поэтому счётчики подсказывают, куда смотреть, а решает сравнение с кабинетом.

## Почему сервер не виден

У сервера две разные привязки. Владение закреплено за пользователем и не меняется при действиях с ключами. Поле `apiKeyId` означает другое — какой ключ управляет сервером сейчас.

Эндпоинты `/v1/infra/*` авторизуют доступ по управляющему ключу: запрос видит только те серверы, у которых `apiKeyId` совпадает с вызывающим ключом. Личный кабинет опирается на владение, поэтому там сервер остаётся виден. Отсюда и расхождение: в кабинете сервер есть, через API с новым ключом его нет.

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

## Как вернуть доступ

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

1. Откройте раздел [Серверы Black Hole](/black-hole).
2. Найдите нужный сервер в списке.
3. В меню карточки сервера выберите **Сменить управляющий ключ**.
4. Выберите в списке новый ключ и подтвердите.

Если у сервера нет управляющего ключа, на его карточке появляется кнопка **Восстановить доступ**, которая открывает тот же выбор. Когда карточка показывает **Владение потеряно**, сначала нажмите **Вернуть себе** — это вернёт сервер во владение, после чего появится выбор ключа. Бесхозные серверы видит и возвращает себе только администратор портала: остальным `POST /api/servers/:id/adopt` отвечает `403 ADMIN_ONLY`, и карточка им не показывается.

Смена затрагивает только управляющую привязку. Виртуальная машина, данные, настройки и субдомен остаются нетронутыми, перезапуск не выполняется. После подтверждения новый ключ сразу видит сервер в `GET /v1/infra/servers` — при условии, что срок его действия не закончился. Набор доступных операций зависит от типа ресурса: отдельной виртуальной машине (`STANDALONE`) доступны и жизненный цикл, и развёртывание, а Galaxy-приложению (`GALAXY_APP`) — развёртывание и удаление, потому что своей виртуальной машины у него нет и будить или перезагружать нечего. Владелец сервера получает сообщение от бота-помощника о том, что управляющий ключ сменён.

## Требования к новому ключу

В списке выбора показываются только подходящие ключи. Ключ подходит, когда выполнены все условия:

| Условие | Значение |
|---------|----------|
| Тип | API-ключ (`vibe_api_`). Ключ авторизации (`vibe_app_`) в качестве управляющего не принимается |
| Назначение | Ключ общего назначения. Выпущенные платформой временные ключи обслуживания и ключи Cowork не принимаются |
| Владелец | Тот же пользователь, которому принадлежит сервер |
| Портал | Тот же портал Битрикс24, что и у сервера |
| Состояние | Отозванный и заблокированный ключи в списке не показываются |
| Скоуп | `vibe:infra` |
| Не текущий | Ключ, который уже управляет этим сервером, в списке не показывается |

Выбирайте ключ, срок действия которого не закончился. Ключ с истёкшим сроком остаётся в списке, и перепривязка на него проходит — но запросы с ним отклоняются с кодом `401 KEY_EXPIRED`, и доступ к серверу придётся восстанавливать заново. Срок действия каждого ключа виден в разделе [Ключи API](/keys).

Если подходящих ключей нет, создайте новый в разделе [Ключи API](/keys) со скоупом `vibe:infra` — форма позволяет сделать это, не покидая диалог смены ключа.

## Когда это нужно

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

**Управляющий ключ на месте, но не работает.** Привязка сервера цела, нерабочим стал сам ключ:

1. **Секрет ключа потерян.** Ключ действует, но его значение не сохранили при создании. Значение ключа показывается один раз, восстановить его нельзя.
2. **Ключ отозван.** Ключ переведён в состояние `REVOKED` — владельцем, например после компрометации, или платформой при отключении самостоятельно размещённого портала: отключение отзывает ключи пользователя, привязку сервера оно не трогает.
3. **Ключ истёк.** Срок действия закончился, запросы отклоняются с кодом `401 KEY_EXPIRED`.

Во всех трёх случаях порядок обычный — смена управляющего ключа на новый.

**Сервер остался без управляющего ключа.** Поле `apiKeyId` пусто, владелец сохранён. Карточка показывает кнопку **Восстановить доступ**, которая открывает тот же выбор ключа. Удалить ключ с работающими серверами вручную нельзя — это запрещает проверка при удалении.

**Сервер остался и без владельца.** Оба поля пусты. Так бывает после удаления учётной записи владельца: его ключи удаляются вместе с ней, и `userId` с `apiKeyId` обнуляются одновременно. Карточка показывает **Владение потеряно** — сначала нажмите **Вернуть себе**, и только после этого появится выбор ключа. Такие серверы видит только администратор портала.

Возврат владения работает для отдельных виртуальных машин (`STANDALONE`). Galaxy-приложение, оставшееся без владельца, вернуть себе нельзя — платформа помечает его удалённым. Пока владелец у приложения сохранён, управляющий ключ ему меняется обычным порядком.

## Ошибки

| HTTP | Код | Когда возвращается |
|------|-----|---------------------|
| 401 | `KEY_EXPIRED` | Срок действия ключа закончился |
| 401 | `KEY_INACTIVE` | Ключ отозван или заблокирован |
| 404 | `NOT_FOUND` | Обращение к серверу ключом, который им не управляет |
| 403 | `WRONG_KEY` | Развёртывание на сервер ключом, который им не управляет |
| 409 | `KEY_HAS_ACTIVE_SERVERS` | Удаление ключа, на котором есть активные серверы |
| 409 | `KEY_HAS_LINKED_AGENT` | Удаление ключа, которым управляется агент или бот |
| 409 | `APP_HAS_ACTIVE_SERVERS` | Удаление приложения, на ключе которого есть активные серверы |
| 409 | `CONCURRENT_REBIND` | Одновременная смена на разные ключи — сервер уже привязан к другому ключу |

Полный справочник кодов — [Коды ошибок](/docs/errors).

## Известные особенности

**Удаление ключа с активными серверами не проходит.** Запрос возвращает `409 KEY_HAS_ACTIVE_SERVERS`. Смените у этих серверов управляющий ключ — удалять сами серверы для этого не нужно. Полное число серверов приходит в поле `details.activeServerCount` при удалении из кабинета и в `error.details.activeServerCount` у `DELETE /v1/keys/:id`. Список лежит рядом со счётчиком — соответственно в `details.servers` и `error.details.servers`. В списке не больше 10 серверов, самые новые — при значении счётчика больше десяти опирайтесь на него, а остальные серверы смотрите в кабинете. Если на ключе есть ещё и агент или бот, следующая попытка вернёт `409 KEY_HAS_LINKED_AGENT` — перепривязка серверов эту проверку не снимает.

**Смена ключа выполняется только в личном кабинете.** У операции нет варианта в `/v1`: перепривязка работающего сервера — решение человека, а не автоматизированного клиента. Ключ `vibe_api_` этот вызов не выполняет.

**Развёртывание чужим ключом возвращает `403 WRONG_KEY`.** Ответ приходит, когда сервер существует, но управляется другим ключом. В поле `hint` приходит разбор ситуации и указание на смену управляющего ключа как способ восстановления.

**Сервер, созданный приложением, после смены ключа приложению больше не доступен.** Если сервер создан ключом авторизации, сменить управление можно только на личный API-ключ владельца — принять привязку другой ключ авторизации не может. После этого `GET /v1/infra/servers/:id` со старым ключом возвращает `404 NOT_FOUND`, а `POST /v1/infra/servers/:id/deploy` — `403 WRONG_KEY`. Управлять сервером через `/v1` можно только новым личным ключом.

**Порядок относится к серверам и Galaxy-приложениям.** В разделе «Black Hole серверы» показываются отдельные виртуальные машины (`STANDALONE`) и Galaxy-приложения (`GALAXY_APP`). Галактика-хост (`GALAXY`) живёт в разделе «Галактики», и смены управляющего ключа там нет — в `GET /v1/infra/servers` она возвращается наравне с остальными, поэтому по пустому ответу нового ключа отличить её нельзя.

**Одновременная смена ключа с двух устройств разрешается по целевому ключу.** Когда оба запроса выбрали один и тот же ключ, второй запрос возвращает `200` — привязка уже такая, какую он запрашивал. Когда запросы выбрали разные ключи, второй возвращает `409 CONCURRENT_REBIND`: обновите страницу и посмотрите текущую привязку.

## Смотрите также

- [Список серверов](/docs/infra/servers/list)
- [Инфраструктура](/docs/infra)
- [Создание и использование ключа](/docs/keys-auth)
- [Коды ошибок](/docs/errors)
