104 lines
4.9 KiB
Markdown
104 lines
4.9 KiB
Markdown
# Архитектура
|
||
|
||
## Scope
|
||
|
||
Текущий продукт — Android + desktop. iOS исключён. macOS не собирается в первом MVP;
|
||
если платформа вернётся, на ней разрешён только proxy-only до отдельного native review.
|
||
|
||
В приложении две верхние вкладки: `VPN` и `Белые списки`.
|
||
|
||
- `VPN` использует `SignBox` как один движок для стратегий `Основная` (VLESS) и
|
||
`Усиленная` (AmneziaWG 3.1).
|
||
- `Белые списки` использует отдельный `FreeTurn` runtime. FreeTurn не является
|
||
VPN-стратегией SignBox.
|
||
|
||
## Boundaries
|
||
|
||
```text
|
||
Flet/Python
|
||
models, profiles, UI, theme, payment banner, settings registry
|
||
|
|
||
| versioned idempotent commands
|
||
v
|
||
Flet service extension / MethodChannel
|
||
|
|
||
v
|
||
Android native services (separate lifetime from Activity)
|
||
VpnRuntimeService (VpnService + foreground)
|
||
ProxyRuntimeService (foreground + proxy/hotspot)
|
||
|
|
||
+-----+--------------------+
|
||
| SignBox/libbox adapter | FreeTurn mobile adapter
|
||
| TUN/proxy/stats | Start/StartTunnel/Stop/Captcha
|
||
```
|
||
|
||
Python does not start an Android subprocess and does not own VPN lifetime. On desktop,
|
||
the SignBox adapter may own a configured executable with `asyncio` process management;
|
||
FreeTurn desktop is explicitly unsupported.
|
||
|
||
Android state is authoritative in native `SharedPreferences`. Reopening the Flet UI must
|
||
call `get_runtime_state`; missing Python state never implies that a service is stopped.
|
||
The native bridge has this contract. `sing-box-lx` is pinned and runs in-process; the
|
||
service owns `CommandServer`, while `CommandClient` is its local management channel.
|
||
|
||
## Engine abstraction
|
||
|
||
`app/core/interface.py` defines the common lifecycle. `BackendRegistry` and
|
||
`EngineManager` select adapters by registry and enforce one active engine. Capabilities
|
||
are checked before start. Unsupported combinations raise `UnsupportedEngineError` and
|
||
are rendered as `Недоступно`, not as connected.
|
||
|
||
Internals remain different:
|
||
|
||
- SignBox exposes VLESS and AmneziaWG packet/TUN plus sing-box SOCKS/HTTP inbounds.
|
||
`PlatformInterface.openTun` is the only creator of the Android TUN; Python and the
|
||
Activity never own its fd.
|
||
- FreeTurn uses its own mobile binding and event/CAPTCHA lifecycle. Its `Start` API is
|
||
not a generic SOCKS5/HTTP listener; standard sharing remains unsupported until a
|
||
separately implemented listener reports actual bound addresses.
|
||
- `VOID` is an Android VPN data-path policy and must be enforced before an engine or
|
||
direct fallback. It must never be implemented as `addDisallowedApplication`, because
|
||
Android then sends that app directly.
|
||
|
||
## Modes and sharing
|
||
|
||
Desktop is proxy-only in the first release. Android supports VPN, proxy-only, and local
|
||
`Both` with one sing-box instance. Hotspot proxy-only is implemented. `Both +
|
||
LocalOnlyHotspot` remains explicitly unsupported until hotspot discovery and
|
||
VpnService startup are coordinated around the same core.
|
||
|
||
Proxy defaults to `127.0.0.1`; Wi-Fi sharing uses `LocalOnlyHotspot`, discovers its
|
||
actual interface address, and binds listeners only after the hotspot callback. It never
|
||
assumes `192.168.43.1`, does not use `0.0.0.0` as a user default, and does not use
|
||
system tethering/NAT. SOCKS5 and HTTP CONNECT share one optional credential pair.
|
||
|
||
## Backend and devices
|
||
|
||
The backend has separate operator and device authentication:
|
||
|
||
- operator CRUD uses one configured admin Bearer token;
|
||
- each device gets a UUID4 and a separate random credential, stored server-side only
|
||
as SHA-256 hash;
|
||
- enrollment is one-time via an admin-created URL; the first request must use
|
||
`vpnapp/vMAJOR.MINOR/DEVICE_UUID` and binds that User-Agent;
|
||
- subsequent requests use `X-Device-Token` over HTTPS and the same bound User-Agent;
|
||
- UUID/User-Agent alone are not secrets and cannot authenticate.
|
||
|
||
Secrets in SQLite are Fernet-encrypted with `VPNAPP_SECRET_KEY`. The admin HTML never
|
||
lists secret fields after creation. A stronger future option is a hardware-backed key
|
||
and signed requests with replay protection.
|
||
|
||
## Theme and UI
|
||
|
||
The accent is derived from active top-level/strategy context: neutral gray for FreeTurn,
|
||
light blue for Basic, red for Enhanced, while `ThemeMode.SYSTEM` respects the device
|
||
light/dark mode. Screens register their settings sections in `SettingsRegistry`, so the
|
||
settings screen can grow without a universal nullable profile.
|
||
|
||
## Deliberate non-goals
|
||
|
||
- iOS, Personal Hotspot automation and iOS per-app routing.
|
||
- `DIRECT` uses Android package exclusion. `VOID` remains unavailable until native UID
|
||
ownership and the fail-closed packet drop path are implemented and device-tested.
|
||
- bundled native cores without pinned source, license, ABI and API evidence.
|
||
- management/debug listeners exposed by SignBox.
|