Metadata-Version: 2.4
Name: vpn-egressctl
Version: 0.2.0
Summary: Declarative control plane for the sing-box VPN egress gateway
Author: Flamy Studio
License-Expression: MPL-2.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# singbox_glue / vpn-egressctl

`vpn-egressctl` — локальный декларативный control plane для Linux-шлюза
`vpn-egress-gw`. Он получает реквизиты подключения из защищённого Hysteria2 URI,
объединяет их с локальной инфраструктурной policy и полностью генерирует
конфигурацию sing-box.

## Контракт релиза 0.2.0

- строго `sing-box 1.14.0`, Linux build с `with_quic` и `with_gvisor`;
- новые установки HY2XS/Hysteria 2.12.2 используют Gecko по умолчанию;
- Gecko packet profile фиксирован: `512/1200`;
- Salamander полностью поддерживается как явно согласованный compatibility
  fallback;
- TUN DNS использует штатный `dns_mode=hijack` без явного `dns_address` и
  route action `hijack-dns`;
- лицензия исходного кода — Mozilla Public License 2.0.

Salamander не является автоматическим fallback: клиент не может согласовать тип
obfs с сервером. Для новой установки production URI HY2XS должен содержать
`obfs=gecko`; `obfs=salamander` допустим только при таком же явно настроенном
режиме сервера.

## Что обеспечивает проект

- endpoint остаётся DNS hostname и не закрепляется как публичный `/32`;
- bootstrap DNS привязан к `eth0`, remote DoH идёт через `hy2-out`;
- неизвестные URI и policy-параметры отклоняются;
- candidate проходит настоящий `sing-box check` до установки;
- запись атомарна, после неуспешного restart/healthcheck выполняется
  same-release rollback;
- legacy `last-good` без версии и checksum не используется;
- nftables guard блокирует прямой forwarding `eth1 -> eth0`;
- systemd path unit применяет изменение URI без постоянного Python-процесса.

## Только чистая установка

Переход с 0.1.0 на 0.2.0 через `apt upgrade` или `dpkg -i` поверх старого пакета
не поддерживается и блокируется `preinst`. Старые policy, config, state и backups
не мигрируют и не активируются.

Повторная установка пакета ровно 0.2.0 поверх согласованного состояния 0.2.0
допускается; переход с любого другого релиза требует чистой установки.

Правильная последовательность: остановить сервисы, оставить anti-leak guard,
удалить пакет 0.1.0, архивировать его данные, установить sing-box 1.14.0 и пакет
0.2.0, настроить новую policy и применить новый Gecko URI. Полный runbook:
[чистая установка](docs/migration.md).

## Основные команды

```bash
sudo vpn-egressctl import --stdin
sudo vpn-egressctl check
sudo vpn-egressctl diff
sudo vpn-egressctl sync
sudo vpn-egressctl status --json
sudo vpn-egressctl doctor
sudo vpn-egressctl rollback
```

URI нельзя передавать позиционным аргументом: secret не должен попадать в argv и
shell history.

## Документация

- [Архитектура](docs/architecture.md)
- [Конфигурация](docs/configuration.md)
- [Чистая установка 0.2.0](docs/migration.md)
- [Эксплуатация](docs/operations.md)
- [Безопасность](docs/security.md)
- [Контракт sing-box 1.14.0](docs/sing-box-1.14.md)
- [Тестирование](docs/testing.md)
- [Диагностика](docs/troubleshooting.md)

## Локальная проверка

На Linux:

```bash
make check
```

Указанный embedded Python для Windows работает в isolated mode, поэтому пути
добавляются явно:

```powershell
E:\python-31312\python.exe -c "import sys,unittest; sys.path[:0]=[r'F:\projects\singbox_glue',r'F:\projects\singbox_glue\src']; s=unittest.defaultTestLoader.discover('tests',top_level_dir='.'); r=unittest.TextTestRunner(verbosity=2).run(s); raise SystemExit(not r.wasSuccessful())"
```

## Лицензия

Версия 0.2.0 распространяется по [Mozilla Public License 2.0](LICENSE).
