# Remnawave Panel v3.0.0

**URL:** https://f.docs.rw/t/topic/354
**Category:** Panel
**Tags:** dev
**Created:** 2026-07-03T20:38:05Z
**Posts:** 4

## Post 1 by @remnawave — 2026-07-03T20:38:05Z

> [!tip] Важно  
> **Перед обновлением** , обязательно сделать полный бекап ваших данных (база данных, `.env`-файл). [См. Инструменты бекапа и восстановления.](https://docs.rw/awesome-remnawave#backup-restore)

## **На что обратить внимание?**

Это мажорный релиз с серьезными изменениями API. Самое главное: **пользователи больше не идентифицируются по UUID** – везде используется числовой `id`. Кроме того, приведены в порядок коды ответов, все ошибки (`400`, `404`, `500`) типизированы и задокументированы в OpenAPI-спецификации. Если вы используете API напрямую или через сгенерированный SDK – обязательно прочитайте разделы ниже.

* * *

### Новые `.env` переменные

```
// error-start
JWT_AUTH_SECRET=значение_1
// error-end

// success-start
APP_SECRET=значение_1
// success-end
```

Переименуйте переменную `JWT_AUTH_SECRET` на `APP_SECRET` (содержимое оставьте прежним!).

Следующие переменные можно удалить:

```
JWT_API_TOKENS_SECRET=change_me
SWAGGER_PATH=/docs
SCALAR_PATH=/scalar
IS_DOCS_ENABLED=true
```

Документация и инструменты теперь всегда доступны по фиксированным путям:

- Swagger – `/api/backend-tools/swagger`
- Scalar – `/api/backend-tools/scalar`
- Bull Board (очереди) – `/api/backend-tools/queues`

Всё, что находится под `/api/backend-tools`, защищено новой авторизацией. В панели для этого появилась карточка **Backend Tools** (раздел **Remnawave Settings** ) с кнопкой входа в один клик.

### Настройки подписки переехали в Response Headers

Из настроек подписки **удалены поля** `profileTitle`, `profileUpdateInterval`, `supportLink`, `isProfileWebpageUrlEnabled`, `happAnnounce`, `happRouting` (и соответствующие карточки в UI). Их значения автоматически мигрируют в обычные **кастомные заголовки ответа (хедеры)**: `profile-title`, `profile-update-interval`, `support-url`, `profile-web-page-url`, `announce`, `routing`.

Обратите внимание на изменение поведения: раньше заголовок `routing` отправлялся **только клиентам Happ** , теперь настроенные заголовки отправляются **всем клиентам без фильтра по User-Agent**. Значения заголовков поддерживают **Template Variables** и префикс `rwEncodeBase64:` (кодирует значение и отдаёт его как `base64:<...>`).

У внешних сквадов поле `responseHeaders` разделено на `responseHeadersAdd` (добавить заголовки) и `responseHeadersRemove` (список заголовков, которые нужно вырезать из ответа). Существующие значения мигрируют автоматически.

* * *

### Пользователи теперь идентифицируются числовым `id`, поле `uuid` удалено

Поле `uuid` **полностью удалено** из объекта пользователя во всех ответах API. Единственный идентификатор пользователя теперь – числовой `id`. Все пути с `{uuid}` пользователя переехали на `{userId}`:

| Было | Стало | |
| --- | --- | --- |
| `DELETE /api/users/{uuid}` | `DELETE /api/users/{userId}` | |
| `GET /api/bandwidth-stats/users/{uuid}` | `GET /api/bandwidth-stats/users/{userId}` | |
| `GET /api/hwid/devices/{userUuid}` | `GET /api/hwid/devices/{userId}` | |
| `GET /api/subscriptions/by-uuid/{uuid}` | `GET /api/subscriptions/by-id/{userId}` | |
| `GET /api/subscriptions/connection-keys/{uuid}` | `GET /api/subscriptions/connection-keys/{userId}` | |
| `GET /api/users/{uuid}/accessible-nodes` | `GET /api/users/{userId}/accessible-nodes` | |
| `GET /api/users/{uuid}/subscription-request-history` | `GET /api/users/{userId}/subscription-request-history` | |
| `GET /api/users/{uuid}` | `GET /api/users/{userId}` | |
| `GET/PUT /api/metadata/user/{uuid}` | `GET/PUT /api/metadata/user/{userId}` | |
| `POST /api/users/{uuid}/actions/*` | `POST /api/users/{userId}/actions/*` | |

Тела запросов изменились соответственно:

- все bulk-операции над пользователями (`/api/users/bulk/*`) принимают `userIds` (массив чисел) вместо `uuids`;
- HWID-эндпоинты (`/api/hwid/devices*`) принимают `userId` вместо `userUuid`;
- `PATCH /api/users` идентифицирует пользователя по `id` вместо `uuid`;
- `POST /api/users` больше не принимает свой `uuid` при создании;
- `POST /api/users/resolve` – поле `uuid` удалено (используйте `id`, `shortUuid` или `username`).

Поиск по `username` и `shortUuid` (`/api/users/by-username/{username}`,`/api/users/by-short-uuid/{shortUuid}`) не изменился. Если вы где-то храните UUID пользователей – мигрируйте на числовые `id` до обновления интеграций.

### Удалены эндпоинты поиска пользователей по Telegram ID, email, тегу и ID

Следующие эндпоинты **удалены** :

- `GET /api/users/by-telegram-id/{telegramId}`
- `GET /api/users/by-email/{email}`
- `GET /api/users/by-tag/{tag}`
- `GET /api/users/by-id/{id}` (теперь это просто `GET /api/users/{userId}`)

Вместо них используйте **`GET /api/users/stream`** – эндпоинт получил новые query-параметры фильтрации: `telegramId`, `email`, `tag`, а также `status`, `trafficLimitStrategy` и `externalSquadUuid`.

Пример – получить пользователей по Telegram ID: `GET /api/users/stream?telegramId=123456789`

Эндпоинт использует курсорную пагинацию: передавайте `nextCursor` из предыдущего ответа в параметре `cursor`, пока не получите все записи (`size` – до **1000** записей за запрос, по умолчанию **250** ).

### Новый эндпоинт: продление подписки одного пользователя

**`POST /api/users/{userId}/actions/extend`** с телом `{ "days": N }` продлевает срок действия:

- если пользователь `EXPIRED` – новая дата считается от текущего момента, и пользователь становится `ACTIVE`;
- если пользователь `ACTIVE` – дни добавляются к текущей дате истечения.

### Новые эндпоинты статистики

- **`GET /api/system/stats/digest`** – агрегированная сводка за период: созданные и истёкшие пользователи, общий трафик, трафик пользователей, созданных за период, новые HWID-устройства;
- **`GET /api/system/stats/http`** – счётчики HTTP-запросов по роутам;
- **`POST /api/bandwidth-stats/nodes/usage`** – пользователи, потратившие на указанных нодах больше `minTotalBytes` за период;
- **`GET /api/internal-squads/{uuid}/usage`** – потребление трафика пользователями сквада.
- **`GET /api/bandwidth-stats/internal-squads/{squadUuid}/users/{userId}/usage`** – дневное потребление одного пользователя на нодах сквада.

Legacy-эндпоинты статистики (`.../users/{uuid}/legacy`, `.../nodes/{uuid}/users/legacy`) **удалены**.

Также у внутренних сквадов появились операции над **списком** пользователей: `POST .../bulk-actions/add-many-users` и `DELETE .../bulk-actions/remove-many-users` (тело `userIds`, до 1000 за запрос, выполняются в фоне, `202`).

### DELETE-запросы теперь возвращают `204` (или `202`) без тела ответа

Раньше все DELETE-эндпоинты возвращали `200` с телом вида `{ "response": { "isDeleted": true } }`. Теперь:

- **`204 No Content`** – операция выполнена, тело ответа **отсутствует** (удаление пользователя, ноды, хоста, токена и т.д.);
- **`202 Accepted`** – операция принята и выполняется **в фоне** (например, удаление пользователей из сквада).

### Массовые (bulk) операции теперь возвращают `202` или `204` без тела

Bulk-операции над пользователями и нодами (`/api/users/bulk/*`, `/api/nodes/bulk-actions`, `/api/hosts/bulk/*`, добавление/удаление пользователей в сквадах, рестарт нод) больше **не возвращают `{ "affectedRows": N }`** :

- **`202 Accepted`** – операция поставлена в очередь и выполняется в фоне;
- **`204 No Content`** – операция выполнена, тело не возвращается.

Число затронутых записей из ответа узнать больше нельзя – учитывайте это в интеграциях.

### Типизированные ошибки `400`, `404` и `500`

Все эндпоинты теперь документируют ошибки едиными схемами:

- **`400`** – `RemnawaveBadRequestErrorDto` (бизнес-ошибки, с полями `timestamp`, `path`, `message`, `errorCode`) или `RemnawaveValidationErrorDto` (ошибки валидации, с полями `message`, `statusCode`, `errors[]`);
- **`404`** – `RemnawaveNotFoundErrorDto`;
- **`500`** – `RemnawaveInternalServerErrorDto`.

Все возможные значения `message` и `errorCode` перечислены в спецификации per-endpoint.

### `GET /api/keygen`: `pubKey` заменён на `secretKey`

В ответе эндпоинта поле `response.pubKey` заменено на `response.secretKey`.

### Переименован модуль IP: `/api/ip-control` → `/api/connections`

Эндпоинты переехали на новые пути и получили единообразную структуру:

| Было | Стало |
| --- | --- |
| `POST /api/ip-control/fetch-ips/{uuid}` | `POST /api/connections/by-user/{userId}` |
| `GET /api/ip-control/fetch-ips/result/{jobId}` | `GET /api/connections/by-user/{jobId}` |
| `POST /api/ip-control/fetch-users-ips/{nodeUuid}` | `POST /api/connections/by-node/{nodeUuid` |
| `GET /api/ip-control/fetch-users-ips/result/{jobId}` | `GET /api/connections/by-node/{jobId}` |
| `POST /api/ip-control/drop-connections` | `POST /api/connections/drop` |

Логика не изменилась: `POST` запускает фоновую задачу и возвращает `jobId`, `GET` с этим `jobId` отдаёт результат. Старые пути **удалены.** Обратите внимание: `by-user` теперь принимает числовой `userId`, а в теле `POST /api/connections/drop` поле `userUuids` заменено на `userIds`.

**Скоупы API-токенов мигрируют автоматически.** Ресурс `ip-control` переименован в `connections` (`ip-control:fetch-ips` → `connections:by-user`, `ip-control:drop-connections` → `connections:drop` и т.д.). Скоупы существующих токенов будут обновлены при апдейте панели автоматически – ничего делать не нужно. Но если ваша интеграция сама создаёт токены с этими скоупами, обновите строки на новые.

### Внешние сквады: `responseHeaders` разделён на `add`/`remove`

В настройках внешних сквадов поле `responseHeaders` заменено на два: `responseHeadersAdd` (объект «заголовок → значение») и `responseHeadersRemove` (массив имён заголовков). Это касается тела `PATCH /api/external-squads` и объектов сквадов во всех ответах.

### Из настроек подписки удалены некоторые поля

Из `GET/PATCH /api/subscription-settings` удалены поля `profileTitle`, `profileUpdateInterval`, `supportLink`, `isProfileWebpageUrlEnabled`, `happAnnounce`, `happRouting` – эти настройки теперь управляются через “Кастомные хедеры”.

Также из ответа `GET /api/tokens` удалён объект `docs`.

* * *

# Бекенд

## **Новый функционал**

- **Экспорт событий в Redis Streams**  
Опциональный экспорт данных панели во внешние системы. Включается `EXPORT_TO_STREAM_ENABLED=true`, длина стримов ограничивается `EXPORT_TO_STREAM_MAXLEN` (по умолчанию 3000).  
Три стрима: `ioraw:export:user_usage` (данные трафика пользователей по нодам), `ioraw:export:subscription_requests` (запросы подписки; работает даже при `SERVICE_DISABLE_SRH_RECORDS=true`), `ioraw:export:node_connections` (снапшот онлайн-пользователей и их IP по нодам каждые 5 минут, хранится 1 час). Схемы сообщений опубликованы в `@remnawave/backend-contract` и в `OpenAPI`.
- **Сводная статистика за период (digest)**  
`GET /api/system/stats/digest` – созданные и истёкшие пользователи, общий трафик, трафик пользователей, созданных за период, и новые HWID-устройства за диапазон `start, end`.
- **Raw-хост в кастомных примечаниях**  
_Custom remark_ (в настройках подписки и внешних сквадах) может быть _JSON-объектом_ – он валидируется по схеме хоста из raw-формата подписки и вставляется в подписку как «сырой» хост.
- **Дополнительные вебхук-URL для отдельных событий**
- В `notifications-config.yml` у события можно указать `additionalWebhookUrls: [...]` – эти URL добавляются к глобальному `WEBHOOK_URL` только для конкретного события.

## **Исправления**

- **Mihomo: Hysteria2 obfs Gecko** ([#194](https://github.com/remnawave/backend/pull/194))
- **Shadowsocks 2022: валидация ключа** ([#193](https://github.com/remnawave/backend/pull/193))
- **Fallback-хосты в raw-подписке**
- **Запрет пустых outbounds**  
Валидатор Xray-конфига теперь отклоняет конфиг без `outbounds`.
- _…прочие изменения_

## **Улучшения**

- **Zod v4**
- **Лимиты уведомлений расширены до 31 дня**  
`EXPIRATION_NOTIFICATIONS` теперь принимает значения от `−744` до `744` часов (было `±168`), `NOT_CONNECTED_USERS_NOTIFICATIONS_AFTER_HOURS` – до `744`.

---

## Post 2 by @remnawave — 2026-07-03T20:39:18Z

> **API Changelog 2.8.1 vs. 3.0.0**
>
> #### Users are now addressed by numeric `id` — user `uuid` removed
> 
> The user entity is now identified exclusively by its numeric `id`. The `uuid` property was **removed from every user object** in responses (users, users stream, subscriptions, resolve, etc.), and all user-scoped routes changed their path parameter from `{uuid}` to `{userId}` (type `number`). The `telegramId` property type changed from `integer` to `number`.
> 
> Renamed paths:
> 
> - `GET /api/users/{uuid}` → `GET /api/users/{userId}`
> - `DELETE /api/users/{uuid}` → `DELETE /api/users/{userId}`
> - `GET /api/users/{uuid}/accessible-nodes` → `GET /api/users/{userId}/accessible-nodes`
> - `GET /api/users/{uuid}/subscription-request-history` → `GET /api/users/{userId}/subscription-request-history`
> - `POST /api/users/{uuid}/actions/disable` → `POST /api/users/{userId}/actions/disable`
> - `POST /api/users/{uuid}/actions/enable` → `POST /api/users/{userId}/actions/enable`
> - `POST /api/users/{uuid}/actions/reset-traffic` → `POST /api/users/{userId}/actions/reset-traffic`
> - `POST /api/users/{uuid}/actions/revoke` → `POST /api/users/{userId}/actions/revoke`
> - `GET /api/hwid/devices/{userUuid}` → `GET /api/hwid/devices/{userId}`
> - `GET /api/metadata/user/{uuid}` → `GET /api/metadata/user/{userId}`
> - `PUT /api/metadata/user/{uuid}` → `PUT /api/metadata/user/{userId}`
> - `GET /api/subscriptions/by-uuid/{uuid}` → `GET /api/subscriptions/by-id/{userId}`
> - `GET /api/subscriptions/connection-keys/{uuid}` → `GET /api/subscriptions/connection-keys/{userId}`
> - `GET /api/bandwidth-stats/users/{uuid}` → `GET /api/bandwidth-stats/users/{userId}`
> 
> `GET /api/users/by-id/{id}` was removed — `GET /api/users/{userId}` is now the lookup by id. Lookups by `username` and `shortUuid` (`/api/users/by-username/{username}`, `/api/users/by-short-uuid/{shortUuid}`) are unchanged.
> 
> Request bodies changed accordingly:
> 
> - All user bulk operations take `userIds` (array of numbers) instead of `uuids`: `POST /api/users/bulk/delete`, `bulk/update`, `bulk/reset-traffic`, `bulk/revoke-subscription`, `bulk/extend-expiration-date`, `bulk/update-squads`.
> - HWID device bodies take `userId` instead of `userUuid`: `POST /api/hwid/devices`, `POST /api/hwid/devices/delete`, `POST /api/hwid/devices/delete-all`.
> - `PATCH /api/users` identifies the user by `id` instead of `uuid`.
> - `POST /api/users` no longer accepts a custom `uuid`.
> - `POST /api/users/resolve` no longer accepts `uuid` (use `id`, `shortUuid` or `username`).
> - `POST /api/connections/drop` (former `drop-connections`): `dropBy.userUuids` → `dropBy.userIds`.
> 
> Response bodies: `GET /api/hwid/devices/top-users` dropped `userUuid` (use `id`), `GET /api/node-plugins/torrent-blocker/stats` `topUsers\[\].uuid` → `topUsers\[\].userId`, and the `user` object in subscription responses no longer contains `uuid`.
> 
> * * *
> 
> #### Removed user lookup endpoints
> 
> The following endpoints were removed. Use `GET /api/users/stream` with the new filter parameters (or `GET /api/users/{userId}` for the id lookup) instead.
> 
> - `GET /api/users/by-email/{email}`
> - `GET /api/users/by-tag/{tag}`
> - `GET /api/users/by-telegram-id/{telegramId}`
> - `GET /api/users/by-id/{id}`
> 
> * * *
> 
> #### Users stream: new filter parameters
> 
> `GET /api/users/stream` gains new optional query parameters: `telegramId`, `email`, `tag`, `status`, `trafficLimitStrategy` and `externalSquadUuid`. The `cursor` parameter type changed from `string` to `number`, and `size` now has a default of `250` (min `1`, max `1000`).
> 
> Affected endpoints:
> 
> - `GET /api/users/stream`
> 
> * * *
> 
> #### New endpoint: extend a single user's expiration date
> 
> `POST /api/users/{userId}/actions/extend` with body `{ "days": N }` (min `1`). If the user is `EXPIRED`, the new expiration date is calculated from the current date and the user becomes `ACTIVE`; if the user is `ACTIVE`, the days are added to the existing expiration date. Returns `200` with the updated user object.
> 
> Affected endpoints:
> 
> - `POST /api/users/{userId}/actions/extend` (new)
> 
> * * *
> 
> #### Internal squads: targeted bulk membership operations
> 
> New endpoints to add or remove a **specific list of users** in an internal squad (body: `userIds`, 1–1000 items). Both are processed in the background and return `202` with no body. The existing `add-users`/`remove-users` endpoints (which affect **all** users) are unchanged in semantics.
> 
> Affected endpoints:
> 
> - `POST /api/internal-squads/{uuid}/bulk-actions/add-many-users` (new)
> - `DELETE /api/internal-squads/{uuid}/bulk-actions/remove-many-users` (new)
> 
> * * *
> 
> #### Bandwidth stats: new usage endpoints, legacy endpoints removed
> 
> New endpoints:
> 
> - `POST /api/bandwidth-stats/nodes/usage` — users whose total usage on the given nodes (body: `nodesUuids`) over the period is ≥ `minTotalBytes` (query: `start`, `end`, `minTotalBytes`).
> - `GET /api/internal-squads/{uuid}/usage` and `GET /api/bandwidth-stats/internal-squads/{uuid}/usage` — per-user traffic usage scoped to the nodes reachable via the internal squad inbounds, cursor-paginated (`limit` default `250`, `cursor`, `minTotalBytes`).
> - `GET /api/bandwidth-stats/internal-squads/{squadUuid}/users/{userId}/usage` — daily (zero-filled) usage of a single user on the internal squad nodes for a period.
> 
> Removed endpoints:
> 
> - `GET /api/bandwidth-stats/users/{uuid}/legacy`
> - `GET /api/bandwidth-stats/nodes/{uuid}/users/legacy`
> 
> The `topNodesLimit`/`topUsersLimit` query parameters on the remaining bandwidth-stats endpoints are now optional with defaults (`20`/`100`, min `1`).
> 
> * * *
> 
> #### New system stats endpoints
> 
> - `GET /api/system/stats/digest` — aggregated statistics for a datetime range `\[start, end)`: created/expired user counts, total traffic, traffic spent by users created within the range, and new HWID devices.
> - `GET /api/system/stats/http` — HTTP request counters per route (`method`, `route`, `count`) and a total.
> 
> `GET /api/system/stats/bandwidth` gains an optional `tz` query parameter.
> 
> * * *
> 
> #### DELETE endpoints now return `204 No Content`
> 
> All DELETE endpoints previously returned `200` with a JSON body (e.g. `{ "response": { "isDeleted": true } }`). They now return `204` with **no response body**. Check the status code instead of parsing the response.
> 
> Affected endpoints:
> 
> - `DELETE /api/config-profiles/{uuid}`
> - `DELETE /api/external-squads/{uuid}`
> - `DELETE /api/hosts/{uuid}`
> - `DELETE /api/infra-billing/history/{uuid}`
> - `DELETE /api/infra-billing/nodes/{uuid}`
> - `DELETE /api/infra-billing/providers/{uuid}`
> - `DELETE /api/internal-squads/{uuid}`
> - `DELETE /api/node-plugins/{uuid}`
> - `DELETE /api/node-plugins/torrent-blocker/truncate`
> - `DELETE /api/nodes/{uuid}`
> - `DELETE /api/passkeys`
> - `DELETE /api/snippets`
> - `DELETE /api/subscription-page-configs/{uuid}`
> - `DELETE /api/subscription-templates/{uuid}`
> - `DELETE /api/tokens/{uuid}`
> - `DELETE /api/users/{userId}`
> 
> * * *
> 
> #### Background operations now return `202 Accepted`
> 
> Operations that are processed asynchronously now return `202` with **no response body** (previously `200` with a body such as `{ "response": { "affectedRows": N } }`). The number of affected rows is no longer returned.
> 
> Affected endpoints:
> 
> - `POST /api/external-squads/{uuid}/bulk-actions/add-users`
> - `DELETE /api/external-squads/{uuid}/bulk-actions/remove-users`
> - `POST /api/internal-squads/{uuid}/bulk-actions/add-users`
> - `DELETE /api/internal-squads/{uuid}/bulk-actions/remove-users`
> - `POST /api/internal-squads/{uuid}/bulk-actions/add-many-users`
> - `DELETE /api/internal-squads/{uuid}/bulk-actions/remove-many-users`
> - `POST /api/node-plugins/executor`
> - `POST /api/nodes/actions/restart-all`
> - `POST /api/nodes/{uuid}/actions/restart`
> - `POST /api/users/bulk/all/extend-expiration-date`
> - `POST /api/users/bulk/all/reset-traffic`
> - `POST /api/users/bulk/all/update`
> - `POST /api/users/bulk/delete-by-status`
> - `POST /api/users/bulk/reset-traffic`
> - `POST /api/users/bulk/revoke-subscription`
> - `POST /api/users/bulk/update`
> 
> `POST /api/users/bulk/delete-by-status`: the `status` body field is now explicitly required.
> 
> * * *
> 
> #### Synchronous operations without a response body now return `204 No Content`
> 
> These endpoints previously returned `200` with a body; they now return `204` with **no response body**.
> 
> Affected endpoints:
> 
> - `POST /api/nodes/bulk-actions`
> - `POST /api/nodes/bulk-actions/profile-modification`
> - `POST /api/nodes/bulk-actions/update`
> - `POST /api/nodes/{uuid}/actions/reset-traffic`
> - `POST /api/users/bulk/delete`
> - `POST /api/users/bulk/extend-expiration-date`
> - `POST /api/users/bulk/update-squads`
> - `POST /api/hosts/bulk/delete`
> - `POST /api/hosts/bulk/disable`
> - `POST /api/hosts/bulk/enable`
> - `PATCH /api/hosts/bulk/update`
> 
> * * *
> 
> #### Resource-creating POST endpoints now return `201 Created`
> 
> Previously returned `200`. The response body is unchanged.
> 
> Affected endpoints:
> 
> - `POST /api/node-plugins`
> - `POST /api/subscription-page-configs`
> - `POST /api/subscription-templates`
> 
> `POST /api/system/testers/srr-matcher` changed in the opposite direction: it now returns `200` instead of `201` (it does not create a resource).
> 
> * * *
> 
> #### Typed `400`, `404` and `500` error responses
> 
> Error responses are now documented with shared typed schemas on every endpoint:
> 
> - `400` — `oneOf`: `RemnawaveBadRequestErrorDto` (business errors: `timestamp`, `path`, `message`, `errorCode`) or `RemnawaveValidationErrorDto` (validation errors: `message`, `statusCode`, `errors\\\[\\\]`). The previous inline `{ errors, message, statusCode }` object is removed.
> - `404` — `RemnawaveNotFoundErrorDto` (now documented on every endpoint)
> - `500` — `RemnawaveInternalServerErrorDto`
> 
> All possible `message` and `errorCode` values are enumerated in the specification per endpoint. The actual runtime error format is unchanged — this is a documentation/typing improvement that benefits generated SDK clients. The catch-all `default` responses on auth/passkey endpoints were removed.
> 
> Affected endpoints: all.
> 
> * * *
> 
> #### Auth and passkey endpoints: success responses documented
> 
> Success responses (`200`/`201`) are now documented for the auth and passkey endpoints; previously the specification listed only error responses for them.
> 
> Affected endpoints:
> 
> - `POST /api/auth/login`
> - `POST /api/auth/register`
> - `GET /api/auth/status`
> - `POST /api/auth/oauth2/authorize`
> - `POST /api/auth/oauth2/callback`
> - `GET /api/auth/passkey/authentication/options`
> - `POST /api/auth/passkey/authentication/verify`
> - `GET /api/passkeys`
> - `PATCH /api/passkeys`
> - `GET /api/passkeys/registration/options`
> - `POST /api/passkeys/registration/verify`
> 
> * * *
> 
> #### Keygen: `pubKey` replaced with `secretKey`
> 
> The `GET /api/keygen` response object now returns `secretKey` instead of `pubKey` (`GetPubKeyResponseDto` → `GetNodeSecretKeyResponseDto`).
> 
> Affected endpoints:
> 
> - `GET /api/keygen`
> 
> * * *
> 
> #### IP control module renamed: `/api/ip-control` → `/api/connections`
> 
> All endpoints of the former `ip-control` controller moved to a new base path with a unified route structure. The old paths were **removed without deprecation**. Job semantics are unchanged: `POST` starts a background job and returns a `jobId` (`201`), `GET` with that `jobId` returns the result (`200`), `drop` is fire-and-forget (`202`). Note that the user-scoped endpoint now takes the numeric `userId` instead of the user UUID.
> 
> Affected endpoints:
> 
> - `POST /api/ip-control/fetch-ips/{uuid}` → `POST /api/connections/by-user/{userId}`
> - `GET /api/ip-control/fetch-ips/result/{jobId}` → `GET /api/connections/by-user/{jobId}`
> - `POST /api/ip-control/fetch-users-ips/{nodeUuid}` → `POST /api/connections/by-node/{nodeUuid}`
> - `GET /api/ip-control/fetch-users-ips/result/{jobId}` → `GET /api/connections/by-node/{jobId}`
> - `POST /api/ip-control/drop-connections` → `POST /api/connections/drop`
> 
> **API token scopes renamed.** The `ip-control` scope resource is now `connections`:
> 
> - `ip-control:\*` / `ip-control:read` / `ip-control:write` → `connections:\*` / `connections:read` / `connections:write`
> - `ip-control:fetch-ips` → `connections:by-user`
> - `ip-control:fetch-ips-result` → `connections:by-user-result`
> - `ip-control:fetch-users-ips` → `connections:by-node`
> - `ip-control:fetch-users-ips-result` → `connections:by-node-result`
> - `ip-control:drop-connections` → `connections:drop`
> 
> Scopes stored on existing API tokens are migrated automatically during the upgrade. Integrations that hardcode these scope strings when issuing tokens must switch to the new names.
> 
> * * *
> 
> #### External squads: `responseHeaders` split into add/remove
> 
> The `responseHeaders` property was replaced by two properties: `responseHeadersAdd` (object of header name → value) and `responseHeadersRemove` (array of header names). This affects the `PATCH /api/external-squads` request body and the external squad objects in all responses.
> 
> Affected endpoints:
> 
> - `GET /api/external-squads`
> - `GET /api/external-squads/{uuid}`
> - `POST /api/external-squads`
> - `PATCH /api/external-squads`
> - `POST /api/external-squads/actions/reorder`
> 
> * * *
> 
> #### Subscription settings: fields removed
> 
> The following properties were removed from the subscription settings object (request and response): `profileTitle`, `profileUpdateInterval`, `supportLink`, `isProfileWebpageUrlEnabled`, `happAnnounce`, `happRouting`. The `subscriptionSettings` object embedded in external squad responses shrank accordingly.
> 
> Affected endpoints:
> 
> - `GET /api/subscription-settings`
> - `PATCH /api/subscription-settings`
> 
> * * *
> 
> #### API tokens: `docs` object removed
> 
> The `GET /api/tokens` response no longer contains the `docs` object (`enabled`, `scalarPath`, `swaggerPath`); only `tokens` remains.
> 
> Affected endpoints:
> 
> - `GET /api/tokens`
> 
> * * *
> 
> #### Pagination, filtering and stricter parameter typing
> 
> - Table query parameters `filters`, `filterModes`, `globalFilterMode` and `sorting` are now documented on the list endpoints: `GET /api/users`, `GET /api/hwid/devices`, `GET /api/node-plugins/torrent-blocker`, `GET /api/subscription-request-history`.
> - Pagination parameters `size`/`start` gained defaults and min/max limits: 
> - `GET /api/users` — default `25`, max `1000`
> - `GET /api/users/stream` — default `250`, max `1000`
> - `GET /api/subscriptions` — default `25`, max `500`
> - `GET /api/hwid/devices` — default `25`, max `1000`
> - `GET /api/hwid/devices/top-users` — default `5`, max `100`
> - `GET /api/subscription-request-history` — default `25`, max `1000`
> - `GET /api/node-plugins/torrent-blocker` — default `25`, max `1000`
> 
> - `GET /api/infra-billing/history` gains `start`/`size` pagination parameters.
> - All UUID path parameters across the API now declare `format: uuid`.
> - `GET /api/subscriptions/by-short-uuid/{shortUuid}/raw` — `withDisabledHosts` changed from `boolean` to `string` (default `"false"`).
> 
> * * *
> 
> #### SDK schema renames and new stream message schemas
> 
> Request schemas were renamed from `*RequestDto` to `*BodyDto` (e.g. `CreateUserRequestDto` → `CreateUserBodyDto`), many `*ResponseDto` schemas were removed because the corresponding endpoints no longer return a body (`202`/`204`), and the remaining response schemas were renamed to match the new operations (e.g. `GetUserByUuidResponseDto` → `UserResponseDto`, `GetAllUsersResponseDto` → `GetUsersResponseDto`, `FetchIpsResponseDto` → `ConnectionsByUserResponseDto`). This affects generated API clients — regenerate them against the 3.0.0 specification.
> 
> The specification also documents the message formats of the Remnawave data streams as standalone schemas: `RemnawaveNodeConnectionsStreamMessageDto`, `RemnawaveSubscriptionRequestStreamMessageDto`, `RemnawaveUserUsageStreamMessageDto`.

---

## Post 3 by @Spilex — 2026-07-06T23:22:52Z

Ввиду того, что с версии **3.0.0**  **Remnawave** не выполняет сжатие тела ответа - нужно включить его на стороне реверс прокси.

В Caddy достаточно просто добавить encode - это включит дефолтное сжатие zstd и gzip.

```
https://REPLACE_WITH_YOUR_DOMAIN {
// success-start
  encode
// success-end
    reverse_proxy * http://remnawave:3000
}

https://SUBSCRIPTION_PAGE_DOMAIN {
// success-start
  encode
// success-end
    reverse_proxy * http://remnawave-subscription-page:3010
}

:443 {
    tls internal
    respond 204
}
```

В случае Nginx - нужно прописать больше параметров:

```
server {
    server_name PANEL_DOMAIN;

    listen 443 ssl;
    http2 on;
// success-start
  gzip on;
// success-end

    ...
}

server {
    server_name SUBSCRIPTION_PAGE_DOMAIN;

    listen 443 ssl;
    http2 on;
// success-start
  gzip on;
// success-end

    ...
}

// success-start
# Gzip Compression
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types
    application/javascript
    application/json
    application/manifest+json
    application/xml
    font/opentype
    font/eot
    font/otf
    font/ttf
    image/svg+xml
    text/css
    text/javascript
    text/plain
    text/xml;
// success-end
```

Если у вас используется какой-то CDN - изучайте его документацию. Обычно в таком случае рекомендуется отключать сжатие на стороне вашего реверс прокси, потому что сжатие применяет сам CDN.

---

## Post 4 by @remnawave — 2026-07-21T17:29:29Z

# Как обновиться?

> [!danger] Внимание  
> Инструкция ниже написана в первую очередь для тех, кто устанавливал панель и ее компоненты следуя инструкции на сайте (docs.rw).
> 
> Если вы устанавливали панель и ее компоненты каким-либо скриптом (автоустановщиком) – обратитесь в его документацию или группу для более точных и детальных инструкций.

Для начала обязательно ознакомьтесь с изменения в самом первом сообщение.

Откройте `docker-compose.yml`:

```
cd /opt/remnawave && nano docker-compose.yml
```

Пролистайте вниз до секции `remnawave:`

```
services:
  remnawave:
    image: remnawave/backend:2
    container_name: remnawave
    hostname: remnawave
```

Замните версию `:2` на версию `:3`

```
services:
  remnawave:
// success-start
  image: remnawave/backend:3
// success-end
    container_name: remnawave
    hostname: remnawave
```

Сохраните изменения, и перезапустите панель командой:

```
cd /opt/remnawave && docker compose pull remnawave && docker compose down && docker compose up -d && docker compose logs -f
```
