init
This commit is contained in:
141
docs/architecture.md
Normal file
141
docs/architecture.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# VPN Share Architecture
|
||||
|
||||
## Product Contract
|
||||
|
||||
VPN Share shares an Android phone's active VPN with nearby devices through an
|
||||
encrypted application tunnel. The receiving device runs a small companion client
|
||||
that installs or opens its own virtual network interface and routes traffic to
|
||||
the phone automatically.
|
||||
|
||||
The phone app does not replace the user's VPN provider. It preserves the active
|
||||
VPN app and forwards client traffic through Android's default network routing.
|
||||
|
||||
## Non-Negotiable Platform Constraints
|
||||
|
||||
- No root and Android 8+ means no direct manipulation of system NAT, tethering
|
||||
routing tables, or hotspot iptables.
|
||||
- Only one Android app can be prepared as the active `VpnService` owner at a
|
||||
time. The gateway app therefore cannot run a phone-side `VpnService` while
|
||||
WireGuard, OpenVPN, Clash, V2Ray, or another VPN is active.
|
||||
- Android `LocalOnlyHotspot` is local-only and does not provide Internet. VPN
|
||||
Share uses it only as a local transport for the encrypted tunnel.
|
||||
- `VpnService.protect()` must not be used for forwarded gateway sockets because
|
||||
protected sockets bypass VPN routing.
|
||||
|
||||
## Component Diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph AndroidPhone[Android phone]
|
||||
UI[Compose app UI]
|
||||
FG[Gateway foreground service]
|
||||
DET[VpnDetector]
|
||||
DISC[Discovery: QR, mDNS, USB]
|
||||
FFI[Rust FFI adapter]
|
||||
ENG[vpnshare-core]
|
||||
NAT[Userspace TCP/UDP NAT]
|
||||
DNS[DNS forwarder/cache]
|
||||
MTU[MTU/MSS manager]
|
||||
end
|
||||
|
||||
subgraph Clients[Receiving devices]
|
||||
DC[Desktop client]
|
||||
AC[Android client VpnService]
|
||||
IC[iOS Network Extension]
|
||||
end
|
||||
|
||||
DC -->|USB/Wi-Fi/hotspot VSHP| FG
|
||||
AC -->|Wi-Fi/hotspot VSHP| FG
|
||||
IC -->|Wi-Fi/hotspot VSHP| FG
|
||||
UI --> FG
|
||||
FG --> DET
|
||||
FG --> DISC
|
||||
FG --> FFI
|
||||
FFI --> ENG
|
||||
ENG --> NAT
|
||||
ENG --> DNS
|
||||
ENG --> MTU
|
||||
NAT -->|Android sockets| VPN[Existing active VPN app]
|
||||
DNS -->|Android resolver/network DNS| VPN
|
||||
VPN --> Internet[Internet]
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
App[Client application traffic] --> TUN[Client virtual NIC]
|
||||
TUN --> Packetizer[Client VSHP packetizer]
|
||||
Packetizer --> Crypto[Noise session encryption]
|
||||
Crypto --> Transport[USB/Wi-Fi/hotspot transport]
|
||||
Transport --> Gateway[Android gateway service]
|
||||
Gateway --> Engine[Rust engine]
|
||||
Engine --> NAT[NAT / DNS / flow control]
|
||||
NAT --> Socket[Android TCP/UDP sockets]
|
||||
Socket --> ActiveVpn[Existing active VPN]
|
||||
ActiveVpn --> Internet
|
||||
```
|
||||
|
||||
## Main Sequences
|
||||
|
||||
### USB-First Pairing
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Phone
|
||||
participant Desktop
|
||||
participant Engine
|
||||
|
||||
User->>Phone: Tap Share
|
||||
Phone->>Phone: Start foreground gateway
|
||||
Phone->>Phone: Verify active VPN network
|
||||
Desktop->>Phone: Open USB accessory channel
|
||||
Desktop->>Phone: HELLO with client public key
|
||||
Phone->>User: Show pairing code and device name
|
||||
User->>Phone: Approve
|
||||
Phone->>Engine: Create peer lease
|
||||
Phone->>Desktop: AUTH + CONFIG
|
||||
Desktop->>Desktop: Configure virtual NIC/routes/DNS
|
||||
Desktop->>Phone: IP_PACKET frames
|
||||
Phone->>Internet: Forward via active VPN
|
||||
```
|
||||
|
||||
### Reconnection
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
Client->>Gateway: PING session_id, counters
|
||||
Gateway--xClient: Transport interruption
|
||||
Client->>Gateway: RESUME session_id, ticket
|
||||
Gateway->>Gateway: Validate ticket and peer policy
|
||||
Gateway->>Client: CONFIG delta
|
||||
Client->>Gateway: Continue packet frames
|
||||
```
|
||||
|
||||
## Android Domain Model
|
||||
|
||||
- `GatewaySession`: active share session with transport set, peer leases, VPN
|
||||
status, and byte counters.
|
||||
- `PeerDevice`: trusted receiving device with public key, platform, label,
|
||||
first seen, last seen, and revocation status.
|
||||
- `PairingRequest`: short-lived untrusted request created by USB, QR, or mDNS.
|
||||
- `TransportEndpoint`: USB accessory, LAN UDP/TCP, or local-only-hotspot path.
|
||||
- `TunnelLease`: client virtual IPs, DNS gateway, MTU, route set, and expiry.
|
||||
|
||||
## Runtime Policy
|
||||
|
||||
- Gateway starts only from user action and remains foreground while sharing.
|
||||
- Gateway shows a persistent notification with connected peer count and a stop
|
||||
action.
|
||||
- If no active VPN network is detected, the app can still start local pairing
|
||||
but marks Internet sharing unavailable until the VPN appears.
|
||||
- Forwarded traffic never leaves the device through a protected or underlying
|
||||
non-VPN socket unless the user explicitly enables a future advanced bypass
|
||||
mode.
|
||||
|
||||
## Future Desktop Architecture
|
||||
|
||||
The Rust protocol/core crates are shared by Android gateway and desktop clients.
|
||||
Desktop-specific modules only provide virtual NIC, installer, service manager,
|
||||
tray UI, and OS network configuration.
|
||||
41
docs/desktop-migration.md
Normal file
41
docs/desktop-migration.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Future Desktop Client Migration Plan
|
||||
|
||||
## Shared Core
|
||||
|
||||
The Rust crates are already structured so desktop clients can reuse:
|
||||
|
||||
- VSHP frame parsing and encoding.
|
||||
- Peer/session models.
|
||||
- NAT, MTU, DNS, and flow-control logic where applicable.
|
||||
- Transport abstractions.
|
||||
|
||||
## Desktop-Specific Layers
|
||||
|
||||
Windows:
|
||||
|
||||
- Wintun adapter.
|
||||
- Windows service.
|
||||
- MSI/MSIX installer.
|
||||
- Credential Manager key storage.
|
||||
|
||||
Linux:
|
||||
|
||||
- TUN device.
|
||||
- systemd user/system service.
|
||||
- NetworkManager integration where available.
|
||||
- Secret Service key storage.
|
||||
|
||||
macOS:
|
||||
|
||||
- utun interface.
|
||||
- launchd service.
|
||||
- Keychain key storage.
|
||||
- Notarized app bundle.
|
||||
|
||||
## Product Migration Steps
|
||||
|
||||
1. Keep `clients/desktop` as the CLI bring-up tool.
|
||||
2. Add OS-specific virtual NIC crates under `clients/desktop/src/platform`.
|
||||
3. Add installers once USB IPv4 forwarding is stable.
|
||||
4. Add tray UI after service lifecycle is reliable.
|
||||
5. Split GUI from service so crashes do not drop the tunnel.
|
||||
113
docs/protocol.md
Normal file
113
docs/protocol.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# VSHP Protocol Specification
|
||||
|
||||
VSHP is the VPN Share packet tunnel protocol. It carries IP packets from a
|
||||
paired client virtual interface to an Android gateway over USB, Wi-Fi, or
|
||||
hotspot-local transport.
|
||||
|
||||
## Goals
|
||||
|
||||
- Encrypted by default.
|
||||
- Transport independent.
|
||||
- Resumable after USB or Wi-Fi interruption.
|
||||
- Small enough for battery-conscious mobile operation.
|
||||
- Stable enough for third-party open-source clients.
|
||||
|
||||
## Version
|
||||
|
||||
Current version: `VSHP/1`.
|
||||
|
||||
## Frame Header
|
||||
|
||||
All multibyte integers are network byte order.
|
||||
|
||||
```text
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| magic "VSHP" |
|
||||
+---------------+---------------+-------------------------------+
|
||||
| version = 1 | frame_type | flags |
|
||||
+---------------+---------------+-------------------------------+
|
||||
| stream_id |
|
||||
+---------------------------------------------------------------+
|
||||
| packet_number |
|
||||
| |
|
||||
+---------------------------------------------------------------+
|
||||
| payload_length |
|
||||
+---------------------------------------------------------------+
|
||||
| payload... |
|
||||
+---------------------------------------------------------------+
|
||||
```
|
||||
|
||||
Header length: 24 bytes.
|
||||
|
||||
## Frame Types
|
||||
|
||||
- `HELLO = 1`: protocol version, platform, client public key, capabilities.
|
||||
- `AUTH = 2`: Noise handshake payload or resume ticket.
|
||||
- `CONFIG = 3`: virtual IP lease, DNS, MTU, routes, keepalive interval.
|
||||
- `IP_PACKET = 4`: encrypted IPv4/IPv6 packet from or to the client.
|
||||
- `PING = 5`: keepalive and counters.
|
||||
- `RESUME = 6`: session resumption request.
|
||||
- `STATS = 7`: byte, packet, loss, RTT, and battery hints.
|
||||
- `CLOSE = 8`: close code and human-readable reason.
|
||||
|
||||
## Security Handshake
|
||||
|
||||
- First pairing: Noise `XXpsk2`, X25519, ChaCha20-Poly1305, BLAKE2s/HKDF.
|
||||
- Known peer: Noise `IKpsk2` with stored peer static key.
|
||||
- QR/USB pairing PSK is one-time and expires after 2 minutes.
|
||||
- Every encrypted frame binds protocol version, peer id, transport id, and
|
||||
transcript hash.
|
||||
- Replay window: 256 packet numbers per direction.
|
||||
- Rekey: after 1 GiB or 10 minutes, whichever comes first.
|
||||
|
||||
## Pairing URI
|
||||
|
||||
```text
|
||||
vshare://pair?v=1&sid=<session>&server_pk=<base64url>&psk=<base64url>&transports=<csv>&expires=<unix>
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `psk` is 128 bits minimum.
|
||||
- `sid` is random and not reused.
|
||||
- `expires` is enforced by the phone and client.
|
||||
- QR pairing must be confirmed on the phone before a peer is trusted.
|
||||
|
||||
## Client Configuration Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"lease_id": "base64url",
|
||||
"peer_ipv4": "10.241.0.2/32",
|
||||
"peer_ipv6": "fd7a:7670:7368::2/128",
|
||||
"gateway_dns": "10.241.0.1",
|
||||
"routes": ["0.0.0.0/0", "::/0"],
|
||||
"mtu": 1280,
|
||||
"keepalive_ms": 15000,
|
||||
"idle_timeout_ms": 120000
|
||||
}
|
||||
```
|
||||
|
||||
IPv6 routes are omitted unless the gateway verifies upstream IPv6 through the
|
||||
active VPN.
|
||||
|
||||
## Close Codes
|
||||
|
||||
- `0`: normal close.
|
||||
- `1`: unsupported version.
|
||||
- `2`: authentication failed.
|
||||
- `3`: peer revoked.
|
||||
- `4`: VPN unavailable.
|
||||
- `5`: policy denied.
|
||||
- `6`: protocol error.
|
||||
- `7`: overload/backpressure.
|
||||
|
||||
## MTU Policy
|
||||
|
||||
- Default MTU is `1280`.
|
||||
- USB may probe and raise the link MTU.
|
||||
- TCP MSS is clamped to `mtu - 40` for IPv4 and `mtu - 60` for IPv6.
|
||||
- Oversized packets are dropped with client-visible Packet Too Big signaling
|
||||
where possible.
|
||||
49
docs/repository-structure.md
Normal file
49
docs/repository-structure.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Repository and Package Structure
|
||||
|
||||
## Android Project
|
||||
|
||||
```text
|
||||
apps/android/app
|
||||
apps/android/core/domain
|
||||
apps/android/core/engine
|
||||
apps/android/feature/share
|
||||
apps/android/service/gateway
|
||||
```
|
||||
|
||||
## Kotlin Packages
|
||||
|
||||
```text
|
||||
org.vpnshare.app
|
||||
org.vpnshare.domain.model
|
||||
org.vpnshare.engine
|
||||
org.vpnshare.feature.share
|
||||
org.vpnshare.gateway
|
||||
org.vpnshare.gateway.discovery
|
||||
org.vpnshare.gateway.transport
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
|
||||
- Domain module contains no Android platform APIs.
|
||||
- Engine module defines Kotlin-facing core interfaces and FFI adapters.
|
||||
- Gateway service module owns Android network, USB, NSD, hotspot, and
|
||||
foreground-service integration.
|
||||
- Feature modules own Compose UI only.
|
||||
|
||||
## Rust Workspace
|
||||
|
||||
```text
|
||||
crates/vpnshare-proto
|
||||
crates/vpnshare-core
|
||||
crates/vpnshare-transport
|
||||
crates/vpnshare-ffi
|
||||
clients/desktop
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
|
||||
- `vpnshare-proto` owns stable wire format.
|
||||
- `vpnshare-core` owns packet tunnel domain logic.
|
||||
- `vpnshare-transport` owns transport-independent I/O traits.
|
||||
- `vpnshare-ffi` is the narrow C ABI/JNI bridge.
|
||||
- Platform clients bind the Rust core to virtual NIC and OS lifecycle APIs.
|
||||
50
docs/roadmap.md
Normal file
50
docs/roadmap.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# MVP Roadmap
|
||||
|
||||
## M0: Foundation
|
||||
|
||||
- Repository metadata, license, contribution docs.
|
||||
- Android Gradle project and Rust workspace.
|
||||
- Domain model, engine interface, foreground service skeleton.
|
||||
- VSHP frame parser with unit tests.
|
||||
|
||||
## M1: USB-First MVP
|
||||
|
||||
- Android USB accessory transport.
|
||||
- Desktop USB host transport for Linux and Windows.
|
||||
- Encrypted pairing over USB with phone-side approval.
|
||||
- IPv4 TUN/Wintun virtual interface on desktop.
|
||||
- TCP, UDP, and DNS forwarding through the Android gateway.
|
||||
- Basic reconnection with session resume ticket.
|
||||
|
||||
Acceptance:
|
||||
|
||||
- A Windows or Linux laptop paired by USB can browse through the phone's active
|
||||
VPN without manual proxy, route, DNS, or SSH setup.
|
||||
|
||||
## M2: Desktop Productization
|
||||
|
||||
- macOS utun support.
|
||||
- Installers and background service management.
|
||||
- Tray UI with connect/disconnect and status.
|
||||
- Peer revoke and rename UI on Android.
|
||||
- Crash-safe logs without traffic content.
|
||||
|
||||
## M3: Wi-Fi and Hotspot
|
||||
|
||||
- mDNS/DNS-SD discovery.
|
||||
- QR pairing for Wi-Fi.
|
||||
- LocalOnlyHotspot setup flow.
|
||||
- Automatic transport fallback between USB and Wi-Fi.
|
||||
|
||||
## M4: Android Client
|
||||
|
||||
- Android receiving-device app using its own `VpnService`.
|
||||
- QR pairing and one-tap connect.
|
||||
- Per-app include/exclude on receiving Android device.
|
||||
|
||||
## M5: IPv6, Performance, and iOS
|
||||
|
||||
- IPv6 upstream validation and route enablement.
|
||||
- MTU probing, MSS clamping, and adaptive buffers.
|
||||
- iOS Network Extension client.
|
||||
- Battery and throughput tuning on physical-device matrix.
|
||||
70
docs/security.md
Normal file
70
docs/security.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Security Design and Threat Model
|
||||
|
||||
## Security Objectives
|
||||
|
||||
- Only approved devices can use the gateway.
|
||||
- Local attackers cannot read or alter tunneled traffic.
|
||||
- A stolen QR code expires quickly and cannot silently enroll a device.
|
||||
- The gateway does not log browsing history, DNS names, or packet payloads.
|
||||
- A malicious peer cannot access the phone LAN or other peers by default.
|
||||
|
||||
## Trust Boundaries
|
||||
|
||||
- Trusted: phone owner approval, installed VPN app, VPN Share signed binaries.
|
||||
- Semi-trusted: paired devices after explicit approval.
|
||||
- Untrusted: local Wi-Fi, hotspot participants, USB host before approval,
|
||||
mDNS announcements, QR observers.
|
||||
|
||||
## Threats and Mitigations
|
||||
|
||||
| Threat | Mitigation |
|
||||
| --- | --- |
|
||||
| Local MITM on Wi-Fi | Noise authenticated encryption, transcript binding, peer keys |
|
||||
| Stolen QR | One-time PSK, 2-minute expiry, phone-side confirmation |
|
||||
| Malicious paired device | Per-peer revocation, no inbound LAN access, rate limits |
|
||||
| Replay | Monotonic packet numbers and replay windows |
|
||||
| DNS leak | Client DNS points to VPN Share gateway; gateway forwards through Android default VPN network |
|
||||
| VPN bypass | Gateway does not protect forwarded sockets; UI warns if no active VPN is detected |
|
||||
| Battery drain | Foreground service only while active, batched stats, adaptive keepalive |
|
||||
| DoS by paired client | Per-peer queue limits, flow caps, overload close code |
|
||||
|
||||
## Data Collection Policy
|
||||
|
||||
Default telemetry is local only:
|
||||
|
||||
- Connected peer labels.
|
||||
- Bytes in/out.
|
||||
- Session duration.
|
||||
- Error counters.
|
||||
|
||||
VPN Share must not collect:
|
||||
|
||||
- Packet payloads.
|
||||
- Browsing history.
|
||||
- DNS query names.
|
||||
- Destination IP history outside short-lived in-memory flow tables.
|
||||
|
||||
## Key Storage
|
||||
|
||||
- Android gateway identity key is generated on first run and stored in Android
|
||||
Keystore when available.
|
||||
- Peer public keys and labels are stored in app-private encrypted storage.
|
||||
- Desktop clients store their device key in the OS credential store where
|
||||
available, with file-permission fallback.
|
||||
|
||||
## Play Store Compliance
|
||||
|
||||
The gateway mode itself does not use `VpnService`. If Android client mode is
|
||||
shipped in the same package, the release must:
|
||||
|
||||
- Declare the `VpnService` use in the store listing.
|
||||
- Show prominent in-app disclosure before Android client VPN permission.
|
||||
- Explain that traffic is encrypted to the paired VPN Share gateway.
|
||||
- Avoid traffic redirection for ads or monetization.
|
||||
- Submit the Play Console `VpnService` declaration.
|
||||
|
||||
## Foreground Service Compliance
|
||||
|
||||
Active sharing uses a foreground service with `connectedDevice` because it
|
||||
maintains live communication with external devices. The service must expose a
|
||||
persistent notification and stop action.
|
||||
46
docs/testing-ci-play.md
Normal file
46
docs/testing-ci-play.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Testing, CI, and Release Compliance
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- Rust unit tests: frame parsing, NAT expiry, DNS policy, MTU calculations,
|
||||
replay windows, lease allocation.
|
||||
- Rust fuzzing: malformed VSHP frames and packet parser inputs.
|
||||
- Kotlin unit tests: domain state reducers, pairing expiry, VPN status mapping.
|
||||
- Android instrumented tests: foreground service lifecycle, notification stop
|
||||
action, mDNS registration, LocalOnlyHotspot failure paths.
|
||||
- Hardware integration tests: USB accessory sessions on physical Android
|
||||
devices and Windows/Linux/macOS hosts.
|
||||
- E2E matrix: WireGuard, OpenVPN, Clash, V2Ray; IPv4-only, IPv6-capable,
|
||||
split-tunnel VPNs, VPN absent.
|
||||
|
||||
## CI Design
|
||||
|
||||
Required jobs:
|
||||
|
||||
- `rust`: `cargo fmt --check`, `cargo clippy --workspace -- -D warnings`,
|
||||
`cargo test --workspace`.
|
||||
- `android`: Gradle unit tests, lint, assemble debug.
|
||||
- `security`: dependency audit, CodeQL, license scan, SBOM generation.
|
||||
- `docs`: markdown lint and Mermaid render smoke test.
|
||||
|
||||
Physical-device CI is required before release for USB, LocalOnlyHotspot, battery,
|
||||
and vendor VPN compatibility.
|
||||
|
||||
## Play Store Review Checklist
|
||||
|
||||
- Store listing explains VPN Share is a network sharing tool.
|
||||
- If Android client VPN mode is bundled, `VpnService` declaration is submitted.
|
||||
- In-app disclosure is separate from privacy policy and requires affirmative
|
||||
action.
|
||||
- No personal/sensitive traffic collection.
|
||||
- No ad traffic manipulation.
|
||||
- Foreground-service type declaration matches actual use.
|
||||
- Target SDK follows current Play requirements at release time.
|
||||
|
||||
## Release Gates
|
||||
|
||||
- No known traffic leaks in VPN-active E2E tests.
|
||||
- No crash loops on VPN revocation, USB disconnect, Wi-Fi change, or sleep.
|
||||
- Packet parser fuzz corpus runs clean.
|
||||
- Reproducible release build and signed SBOM.
|
||||
- Third-party security review before public 1.0.
|
||||
49
docs/transports.md
Normal file
49
docs/transports.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Transport Designs
|
||||
|
||||
## USB Transport
|
||||
|
||||
USB is the first MVP transport.
|
||||
|
||||
### Phone Side
|
||||
|
||||
- Android uses accessory-mode APIs through `UsbManager.openAccessory`.
|
||||
- The gateway treats USB as an ordered byte stream carrying VSHP frames.
|
||||
- The USB session is accepted only after phone-side pairing approval.
|
||||
- If USB disconnects, the peer lease remains resumable for a short window.
|
||||
|
||||
### Desktop Side
|
||||
|
||||
- The desktop client acts as USB host and opens the Android Open Accessory
|
||||
channel.
|
||||
- The client advertises platform, app version, and public key in `HELLO`.
|
||||
- After `CONFIG`, the client configures the local virtual interface and routes.
|
||||
|
||||
### Failure Modes
|
||||
|
||||
- Cable unplug: transport closes, session enters resumable state.
|
||||
- Host sleeps: session ticket can resume on reconnect.
|
||||
- Unknown host: no IP traffic is accepted before pairing approval.
|
||||
|
||||
## Wi-Fi Transport
|
||||
|
||||
- Discovery uses DNS-SD service type `_vpnshare._udp`.
|
||||
- QR pairing remains the reliable fallback when multicast is blocked.
|
||||
- The tunnel uses the same VSHP frame format as USB.
|
||||
- The gateway never assumes LAN is trusted.
|
||||
|
||||
## Hotspot Transport
|
||||
|
||||
- Android app-created hotspot uses `LocalOnlyHotspot`.
|
||||
- The local-only hotspot does not provide Internet; it only provides local reach
|
||||
to the gateway app.
|
||||
- The client still installs its virtual interface and uses VSHP for all traffic.
|
||||
|
||||
## Transport Selection
|
||||
|
||||
Default order:
|
||||
|
||||
1. USB if connected and paired.
|
||||
2. Known Wi-Fi peer on same LAN.
|
||||
3. Local-only hotspot if the user starts it from the app.
|
||||
|
||||
Session resume can switch transports if the peer key and resume ticket validate.
|
||||
Reference in New Issue
Block a user