Files
obsidian-vault/personal/tech/vless-space-subscription-egress.md
T

409 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: vless-space — клиент 3x-ui с egress через external-подписку
created: 2026-09-15
updated: 2026-09-15
type: tech
namespace: personal
tags: [xray, 3x-ui, x-ui, subscription, outbound, profilegrid, truenas, space]
confidence: high
status: reverted-blocked
related:
- "[[family/plans/reverse-xray-3xui-kraken]]"
- "[[personal/tech/xray-reverse-tunnel-kraken-truenas]]"
- "[[personal/tech/xray-outbound-subscription-3xui]]"
- "[[family/how-to/truenas-infrastructure]]"
---
# `vless-space` — клиент 3x-ui, egress через внешнюю подписку
> ## ❌ ФИНАЛ 2026-09-15: ЗАДАЧА НЕ ВЫПОЛНЕНА. БД ОТКАЧЕНА.
>
> **Система возвращена в исходное состояние** (Alex восстановил из бэкапа). Проверено: `xray-admin` Up, панель HTTP 200, `vpn.mallexxx` HTTP 200, подписки `user1`/`kraken-user` HTTP 200, клиенты `user1`+`kraken-user`, outbound'ов 3.
>
> **Главный вывод сессии: 3x-ui НЕЛЬЗЯ править снаружи работающего контейнера.** Четыре подхода — все отбились. Подробно: [[personal/tech/xray-outbound-subscription-3xui]] §«Почему внешняя правка не работает».
>
> **Что осталось полезного:** compose-файл восстановлен (см. [[family/plans/reverse-xray-3xui-kraken]]), шаблон с подпиской собран и лежит готовым, питфоллы задокументированы.
>
> **Единственный рабочий путь к цели:** панель 3x-ui (Inbounds → Edit → Add Client → Save → Restart Xray) **или** API-токен панели. Пароль панели (`vpn-admin`) — bcrypt, недоступен агенту; API-токен Alex не выдал.
## Что хотели
Добавить в 3x-ui (`xray-admin` на TrueNAS) **третьего клиента** `vless-space`, чей трафик уходит в интернет **не** через TrueNAS (`direct`) и **не** через Кра́кена (`via-kraken`), а через **внешнюю подписку** провайдера `profilegrid.net` (28 строк → 10 уникальных серверов).
Требование Alex (дословно): «не трогая `user1` и `kraken-user` (reverse)», «авто брать» — т.е. **балансировщик по всем серверам подписки**, а не один конкретный.
## Ключевое различие, которое я сначала понял неверно
**Подписка — функция КЛИЕНТА, не сервера.** 3x-ui — это сервер (принимает входящие), он **не может** «ходить через чужую подписку» в inbound-режиме.
**НО** у 3x-ui есть `outbound_subscriptions` — функция, которая **скачивает подписку и превращает её серверы в свои outbound'ы**. Тогда сервер может: принять клиента → отправить его трафик через сервер из подписки. Это и есть нужный механизм.
**Схема:**
```text
[клиент vless-space] → vpn.mallexxx.duckdns.org:443 → Caddy → xray-admin:10095
routing: user=[vless-space]
space-balancer (leastPing)
10 × VLESS+REALITY outbound (mirrorgrid.net)
интернет
```
## Подписка (источник серверов)
- **URL:** `https://go.profilegrid.net/sub/djMsNDc3MjgsMTc4OTQ1OTg5NA.WjZEVaps9Xl3s5dkD6x7KRyNIGi7MlDUNAVPMKBUOdk`
- **Проверено 2026-09-15 с TrueNAS:** `HTTP 200`, 12 412 байт, `text/plain`, base64
- **28 строк** `vless://`, из них **10 уникальных серверов** (остальное — дубли одного UUID на тех же хостах)
- **Один UUID на весь список:** `c67ce742-d94f-4e56-889e-9f894ae59940` (на разных хостах встречаются варианты `…-4e56-…` и `…-0008-…`)
- **Формат:** VLESS + `security=reality` + `type=tcp` + `flow=xtls-rprx-vision` + `fp=firefox`
- **Уникальность** определяется парой `host` + `pbk` (publicKey) + `sid` (shortId) — у каждого сервера свои.
### 10 уникальных серверов
| tag | имя | host |
|---|---|---|
| `space-01` | 🇩🇪 Germany | `edge-de.mirrorgrid.net:443` |
| `space-02` | 🇱🇻 Latvia \| YT | `edge-lv.mirrorgrid.net:443` |
| `space-03` | 🇳🇱 Netherlands \| YT | `edge-nl.mirrorgrid.net:443` |
| `space-04` | 🇳🇱 Netherlands #2 | `packages-nl.repocache.com:443` |
| `space-05` | 🇪🇪 Estonia \| YT | `edge-ee.mirrorgrid.net:443` |
| `space-06` | 🇸🇪 Sweden \| YT | `packages-se.repocache.com:443` |
| `space-07` | 🇲🇩 Moldova \| Torrent | `md.repodelivery.com:443` |
| `space-08` | 🇵🇱 Poland \| YT | `edge-pl.mirrorgrid.net:443` |
| `space-09` | 🇫🇷 France \| YT | `edge-fr.mirrorgrid.net:443` |
| `space-10` | 🇺🇸 USA \| YT | `edge-us.mirrorgrid.net:443` |
## Артефакты (на Mac)
```
~/tmp-xray-space/
├── apply_space_v2.sql ← ИСПРАВЛЕННЫЙ SQL (применять этот)
├── apply_space.sql ← первая (битая) версия — НЕ использовать
├── make_sql.py ← генератор v1 (битый)
├── make_sql_v2.py ← генератор v2 (с PRAGMA journal_mode)
├── build_template.py ← сборка xrayTemplateConfig из подписки
├── sub_decoded.txt ← развёрнутая подписка (28 строк vless://)
├── space_outbounds.json ← 10 новых outbound (справочно)
├── template_config.new.json ← новый xrayTemplateConfig (13 outbound)
├── template_config.json ← исходный шаблон из БД
├── runtime_config.json ← снимок /app/bin/config.json
├── xui_copy.db ← копия БД до правок
└── docker-compose.yml ← восстановленный compose xray-admin
```
## Параметры клиента
| | |
|---|---|
| email | `vless-space` |
| UUID | `a792c483-07e2-4723-9c50-78054c0abc07` |
| subId | `24df9391356b48ff` |
| sub-ссылка | `https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff` |
| inbound | `in-10095-tcp` (id=1), port 10095, VLESS-WS |
| egress | `space-balancer` → 10 серверов подписки |
## Изменения в `xrayTemplateConfig`
Шаблон хранится в БД: таблица `settings`, ключ **`xrayTemplateConfig`**. 3x-ui генерирует из него `/app/bin/config.json` при старте.
Добавлено (существующее не тронуто):
**1. Routing-правило** (после `kraken-user-via-reverse`):
```json
{
"type": "field",
"user": ["vless-space"],
"outboundTag": "space-balancer",
"ruleTag": "vless-space-via-subscription"
}
```
**2. Балансировщик:**
```json
"balancers": [
{
"tag": "space-balancer",
"selector": ["space-"],
"strategy": { "type": "leastPing" }
}
]
```
**3. Observatory** (нужен для `leastPing`):
```json
"observatory": {
"subjectSelector": ["space-"],
"probeUrl": "https://www.google.com/generate_204",
"probeInterval": "30s",
"enableConcurrency": true
}
```
**4. 10 outbound'ов** `space-01``space-10`, формат:
```json
{
"tag": "space-NN",
"protocol": "vless",
"settings": { "vnext": [ { "address": "<host>", "port": 443, "users": [
{ "id": "c67ce742-…", "encryption": "none", "flow": "xtls-rprx-vision" } ] } ] },
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"serverName": "<host>", "fingerprint": "firefox",
"publicKey": "<pbk>", "shortId": "<sid>", "spiderX": "<spx>"
}
}
}
```
**Итого:** было 3 outbound (`direct`, `blocked`, `via-kraken`) → стало 13. Правил было 4 → стало 5.
## 🔴 ПРОВАЛ ПОПЫТКИ 1 (2026-09-15) — `database disk image is malformed`
**Симптом:** после применения `apply_space.sql` запрос к БД вернул `Parse error in 2nd command line argument: database disk image is malformed (11)`.
**Причина (моя ошибка в процедуре, не в SQL):**
1. SQL применялся к **копии** базы (`/tmp/x-ui.db.work`), созданной **без** её `-wal`/`-shm`.
2. Копия клалась обратно как `/data/x-ui.db`.
3. Рядом на хосте оставались **СТАРЫЕ** `x-ui.db-wal` и `x-ui.db-shm` от предыдущего процесса.
4. SQLite при открытии увидела WAL-файл, попыталась применить **чужой журнал** к новой базе → `malformed`.
**Дополнительно:** в SQL не было `PRAGMA journal_mode=DELETE` — WAL-режим не отключался на время записи.
> 🔴 **ПИТФОЛЛ (повторяемый):** при правке SQLite-базы, работающей в WAL-режиме, **нельзя** копировать только `.db` и класть рядом со старыми `-wal`/`-shm`. Либо работать **напрямую** с базой при остановленном процессе, либо удалять `-wal`/`-shm` до открытия.
## ✅ ИСПРАВЛЕННАЯ ПРОЦЕДУРА (`apply_space_v2.sql`)
Отличия от v1:
- `PRAGMA journal_mode=DELETE;` в начале → WAL отключён на время сессии
- `PRAGMA journal_mode=WAL;` в конце → возврат к режиму, которого ждёт 3x-ui
- `INSERT OR REPLACE` вместо `INSERT` (идемпотентность)
- **Работа идёт НАПРЯМУЮ с `/data/x-ui.db`**, не через копию
### Команды
```bash
# 1) залить SQL
scp ~/tmp-xray-space/apply_space_v2.sql truenas_admin@mallexxx.duckdns.org:/tmp/
# 2) стоп (3x-ui сам сбросит WAL)
ssh truenas_admin@mallexxx.duckdns.org 'docker stop xray-admin'
# 3) бэкап
ssh truenas_admin@mallexxx.duckdns.org '
TS=$(date +%Y%m%d-%H%M%S)
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/d -v /mnt/RED_2TB/docker/backups:/b alpine sh -c "
mkdir -p /b/xray-admin-v2-\$TS && cp -av /d/x-ui.db /b/xray-admin-v2-\$TS/
echo BACKUP_DIR=/b/xray-admin-v2-\$TS"'
# 4) применить — ВАЖНО: rm WAL до SQL, работа напрямую
ssh truenas_admin@mallexxx.duckdns.org '
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/data -v /tmp:/src alpine sh -c "
apk add --no-cache sqlite >/dev/null 2>&1
cd /data
rm -f x-ui.db-wal x-ui.db-shm
echo \"before=\$(sqlite3 x-ui.db \\\"PRAGMA integrity_check;\\\")\"
sqlite3 x-ui.db < /src/apply_space_v2.sql
echo \"sql_exit=\$?\"
echo \"after=\$(sqlite3 x-ui.db \\\"PRAGMA integrity_check;\\\")\"
sqlite3 x-ui.db \"select email, sub_id from clients order by id;\"
chown 950:root x-ui.db"'
# 5) старт
ssh truenas_admin@mallexxx.duckdns.org 'docker start xray-admin && sleep 6 && docker logs --tail 12 xray-admin'
# 6) проверка
ssh truenas_admin@mallexxx.duckdns.org '
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -r ".inbounds[].settings.clients[]?.email"
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -r ".outbounds | length"
docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -c ".routing.balancers"
curl -sk -o /dev/null -w "HTTP %{http_code}\n" "https://vpn-panel.mallexxx.duckdns.org/sub/24df9391356b48ff"'
```
**Ожидаемо:** клиенты `user1`, `kraken-user`, `vless-space` / outbounds `13` / balancer `space-balancer` / `HTTP 200`.
### Откат
```bash
# A — из бэкапа шага 3
ssh truenas_admin@mallexxx.duckdns.org '
docker stop xray-admin
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/d -v /mnt/RED_2TB/docker/backups:/b alpine sh -c "
cp -av /b/xray-admin-v2-<TS>/x-ui.db /d/x-ui.db
rm -f /d/x-ui.db-wal /d/x-ui.db-shm && chown 950:root /d/x-ui.db"
docker start xray-admin'
# B — из исходного бэкапа (до всех правок)
# /mnt/RED_2TB/docker/backups/xray-admin-before-vless-space-20260915-012057/
# (только x-ui.db; -wal/-shm удалить, не копировать)
```
`user1` и `kraken-user` **не изменяются ни в одной версии SQL** — откат нужен только если сломалась БД.
## ✅ ПОПЫТКА 2 (2026-09-15, ночь-4) — SQL применён, БД корректна, но клиент не в конфиге
**Выполнено (Alex сам, вручную на TrueNAS, строки без `ssh`-обёртки — так ему удобнее):**
Ошибка экранирования в моей инструкции: `\"` внутри `ssh '... sh -c \"...\"'` ломается на `sh`. Результат: диагностические `echo "integrity_..."` упали с `unrecognized token: ""PRAGMA"`, но **сам SQL прошёл** (`sql_exit=0`, вывод `delete` + `wal` = сработали оба `PRAGMA journal_mode`).
**Факт-состояние БД после применения (все три места корректны):**
| Где | `vless-space` |
|---|---|
| таблица `clients` | ✅ `id=5`, `sub_id=24df9391356b48ff`, `enable=1`, `security=auto` |
| `inbounds.settings` JSON (id=1) | ✅ `enable=true`, `id=a792c483-…` |
| `PRAGMA integrity_check` | ✅ `ok` |
| таблица `client_traffics` | ❌ **пустая** (но и у `user1`/`kraken-user` тоже пустая — не критерий) |
**Сгенерированный `/app/bin/config.json` после рестарта:**
```json
{"inbounds":[{"tag":"api","port":62789,"clients":[]},
{"tag":"in-10095-tcp","port":10095,"clients":["user1","kraken-user"]}],
"outcount":13,
"bal":[{"tag":"space-balancer","selector":["space-"],"strategy":{"type":"leastPing"}}]}
```
**шаблон применился** (13 outbound, балансировщик на месте), **клиент — нет**.
**Таймстампы опровергли гипотезу «не перечитал БД»:** `config.json` = 15:56, `x-ui.db` = 15:53, т.е. 3x-ui сгенерировал конфиг ПОСЛЕ правки и всё равно клиента выкинул.
### ❌ «КОРЕНЬ» (`NULL` в `auth`/`reverse`) — ОПРОВЕРГНУТ
Найденное расхождение выглядело убедительно:
```
user1 auth="" reverse=""
kraken-user auth="" reverse=""
vless-space auth=NULL reverse=NULL
```
**Но исправление не помогло.** Подробности провала:
| Шаг | Что сделано | Результат |
|---|---|---|
| 1 | `UPDATE ... SET auth="", reverse=""` (двойные кавычки) | `Parse error: no such column: ""` — SQLite ждёт **одинарные** |
| 2 | `char(39)\|\|char(39)` | дало **две** кавычки: `''''''` вместо `''` — моя ошибка, `char(39)` уже кавычка |
| 3 | Дописан `security:"auto"` в `inbounds.settings` (у `vless-space` его не было, у живых — есть) | ✅ применено (`sql_exit=0`, три `security:auto`) |
| 4 | `docker start` | ❌ **клиент всё равно не в `config.json`** |
**Вывод: `NULL`/`security` — не причина.** БД после всех правок была корректна во всех трёх местах, `config.json` новее БД — и 3x-ui всё равно рендерил только `user1`+`kraken-user`.
### 🔑 НАСТОЯЩАЯ ПРИЧИНА — 3x-ui не берёт клиентов из БД при внешней правке
Два факта, закрывающих вопрос:
1. **`x-ui.db` при живом контейнере показывал СТАРУЮ дату** (15:00) — правки, применённые Alex'ом в 15:53/16:02/16:06, **в основной файл не попадали**. 3x-ui держит базу открытой и пишет в `-wal`. Удаление `-wal` перед правкой **уничтожало актуальные данные** 3x-ui, а после правки он перезаписывал файл из своего состояния.
2. **`config.json` схлопнулся до 2978 б** (было 11 990) — при рестарте 3x-ui перегенерировал конфиг **из внутренней копии**, а не из файла на диске. Отсюда парадокс: 13 outbound'ов применились (шаблон `settings` он читает), а третьего клиента нет (список клиентов берёт из своей памяти).
> 🔴 **ГЛАВНЫЙ ПИТФОЛЛ СЕССИИ (повторяемый):** `inbounds` в 3x-ui **нельзя править через SQLite снаружи**. Работает только `settings.xrayTemplateConfig` (outbound'ы/правила) — и то как «дополнение». Всё, что касается **клиентов инбаунда**, идёт **только через панель** (`/panel/api/inbounds/update/:id`) или через API-токен.
>
> **Признак, что правка не применилась:** `ls -la /app/bin/config.json /etc/x-ui/x-ui.db` — если БД показывает время раньше правки, 3x-ui её не перечитал.
### Как это делалось раньше (`kraken-user`, 2026-09-02)
Из Zulip, запись 2026-09-02 12:00 (сессия «per-user egress»):
> «Persistent global template is stored in `settings.key=xrayTemplateConfig`; **do not edit `/app/bin/config.json` manually because it is generated**»
> «Temporary full-admin API token **не создавался**; `api_tokens` осталась пустой»
> «Verified with the local `xray-test-client`»
`kraken-user` добавлялся **при участии панели 3x-ui** (упоминание `api_tokens` и верификация через клиента это подтверждают). Именно этого доступа в текущей сессии не было.
### Исправление (4 команды, ждут выполнения)
```bash
docker stop xray-admin
```
```bash
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/data alpine sh -c 'apk add --no-cache sqlite >/dev/null 2>&1; cd /data; rm -f x-ui.db-wal x-ui.db-shm; sqlite3 x-ui.db "UPDATE clients SET auth=\"\", reverse=\"\", flow=\"\" WHERE email=\"vless-space\";"; sqlite3 -header x-ui.db "select email, quote(auth), quote(reverse), quote(flow) from clients;"; chown 950:root x-ui.db'
```
```bash
docker start xray-admin
```
```bash
sleep 8; docker exec xray-admin cat /app/bin/config.json | /usr/bin/jq -r '.inbounds[].settings.clients[]?.email'
```
**Ожидаемо:** `user1`, `kraken-user`, `vless-space`.
> ⚠️ **Правило взаимодействия (Alex, 2026-09-15):** команды выдаются **строками без `ssh truenas_admin@… '…'`-обёртки** — Alex выполняет их сам, сидя на хосте. Вложенные кавычки внутри `ssh '... sh -c \"...\"'` ломают передачу (`unrecognized token`), поэтому диагностику и правки писать **плоскими однострочниками**, без экранирования внутри `sh -c`.
### Откат
```bash
docker stop xray-admin
```
```bash
docker run --rm -v /mnt/RED_2TB/docker/xray-admin:/d -v /mnt/RED_2TB/docker/backups:/b alpine sh -c 'cp -av /b/xray-admin-before-vless-space-20260915-012057/x-ui.db /d/x-ui.db; rm -f /d/x-ui.db-wal /d/x-ui.db-shm; chown 950:root /d/x-ui.db'
```
```bash
docker start xray-admin
```
Возвращает состояние «только `user1` + `kraken-user`, 3 outbound».
## Открытые риски (проверено)
1. **`leastPing` + `observatory`** — ✅ **принимаются** (3x-ui 3.7.0 / Xray 26.7.28). Проверено фактом: после применения шаблона контейнер поднялся, `config.json` содержит `balancers` с `leastPing`, Xray-процесс запущен (`bin/xray-linux-amd64 -c bin/config.json`), в логах ошибок нет — только безобидные `WARNING common/protocol/http: received "X-Forwarded-For" … "sockopt.trustedXForwardedFor" is not configured` (Caddy шлёт XFF, на работу не влияет).
2. **~~`auth`/`reverse` = NULL~~** — ❌ **опровергнуто**, не было причиной. См. «Корень опровергнут».
3. **Ссылка подписки `vpn-panel…/sub/<subId>` открыта БЕЗ авторизации** — любой, кто знает `subId`, получит ключ. Кандидат на включение подписки-по-токену в панели (не сделано).
4. **🔴 Доступа к панели 3x-ui у агента нет** — единственный реальный блокер задачи. Пароль bcrypt, API-токен не выдан.
## Что НЕ сделано / как довести до конца
- [x] ~~Применить `apply_space_v2.sql`~~ → применён, БД была корректна
- [x] ~~Исправить `auth`/`reverse`/`flow` с `NULL`~~ → исправлено, **не помогло** (теория опровергнута)
- [x] ~~Дописать `security:"auto"` в `inbounds.settings`~~ → применено, **не помогло**
- [x] ~~Восстановить compose-файл~~**сделано** (см. [[family/plans/reverse-xray-3xui-kraken]])
- [x] ~~Откатить БД~~**сделано Alex'ом**, система в исходном состоянии
- [ ] **❌ ТЕКУЩИЙ БЛОКЕР: нет доступа к панели 3x-ui.** Логин `vpn-admin`, пароль — bcrypt-хэш (`$2a$10$JJscByJjUHJyqmp1a8LZ7egLY0g.XbIoY…`), нечитаем. `secret` из БД (`RuERf2DTzPTw3CoMcVjk7tGXKCQOk0Z4`) для логина не подошёл (403), API-путь вернул 404.
- [ ] **Довести задачу одним из двух:** (а) Alex добавляет клиента в панели — Inbounds → `vless-ws` (10095) → Edit → Add Client (`vless-space` / `a792c483-07e2-4723-9c50-78054c0abc07` / subId `24df9391356b48ff`) → Save → **Restart Xray**; (б) Alex создаёт API-токен (Settings → API Tokens) и агент делает всё сам через `/panel/api/inbounds/update/1`.
- [ ] После появления клиента — routing-правило на `space-balancer` **тоже через панель** (Xray → Routing)
- [ ] Проверить end-to-end: egress IP ≠ `90.189.160.148` (TrueNAS) и ≠ `92.62.70.41` (Kraken)
- [ ] Решить вопрос с авторизацией на sub-ссылках (открыты без авторизации)
- [ ] Обновить [[family/how-to/truenas-infrastructure]] (новый клиент + балансировщик)
- [ ] **Отдельная задача, НЕ начата:** `hermes-taiga` ходит через `vless-proxy`, чей outbound указывает на **мёртвый** `v.qentra.top` (VPS удалён). Переключение на живой сервер — правка 5 полей в `/mnt/RED_2TB/docker/vless-proxy/config.json` (таблица в [[personal/tech/xray-outbound-subscription-3xui]] §попутные находки). Alex спрашивал — **ответ: да, можно**.
## Артефакты на Mac — итоговые (`~/tmp-xray-space/`)
| Файл | Назначение |
|---|---|
| **`apply_final.sql`** | **ФИНАЛЬНЫЙ SQL** (13 447 б): клиент + инбаунд + шаблон с 10 серверами + правило + балансировщик + запись в `outbound_subscriptions`. Не применён. |
| `make_final.py` | генератор `apply_final.sql` |
| `inbound_settings.json` | снимок `inbounds.settings` из живой БД (2 клиента) |
| `tpl_live.json` / `template_config.json` | шаблон из живой БД (2129 б, 3 outbound, 4 правила) |
| `template_config.new.json` | шаблон с 13 outbound (v1, без правки инбаунда) |
| `apply_space_v2.sql` / `apply_space.sql` | SQL v2 (с `PRAGMA journal_mode`) / v1 (битый) |
| `make_sql_v2.py` / `make_sql.py` | генераторы v2 / v1 |
| `fix_security.sql` | правка `security` в `inbounds.settings` (применена, не помогла) |
| `make_fix_security.py` | генератор |
| `build_template.py` | парсер `vless://` → xray-outbound |
| `sub_decoded.txt` | подписка развёрнутая (28 строк) |
| `space_outbounds.json` | 10 outbound'ов (отчёт) |
| `runtime_config.json` | снимок `/app/bin/config.json` (3 клиента, 3 outbound) |
| `xui_copy.db` / `test_apply.db` | копия живой БД / песочница |
| `docker-compose.yml` | **восстановленный compose `xray-admin`** |
| `inbound_settings.json` | снимок инбаунда для финального SQL |
## Связанные заметки
- [[family/plans/reverse-xray-3xui-kraken]] — reverse-туннель к Кра́кену, история compose-инцидента
- [[personal/tech/xray-reverse-tunnel-kraken-truenas]] — архитектура reverse
- [[family/how-to/truenas-infrastructure]] — инфраструктура TrueNAS, контейнер `xray-admin`
- [[family/how-to/vps-qentra]] — удалённый VPS (объясняет, почему `vless-proxy` мёртв)