alle-proxy 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. alle_proxy-0.1.0/.github/workflows/ci.yml +35 -0
  2. alle_proxy-0.1.0/.github/workflows/publish.yml +77 -0
  3. alle_proxy-0.1.0/.gitignore +14 -0
  4. alle_proxy-0.1.0/LICENSE +21 -0
  5. alle_proxy-0.1.0/PKG-INFO +286 -0
  6. alle_proxy-0.1.0/README.md +266 -0
  7. alle_proxy-0.1.0/THIRD_PARTY_NOTICES.md +43 -0
  8. alle_proxy-0.1.0/assets/wordmark.svg +37 -0
  9. alle_proxy-0.1.0/docs/cli-reference.md +342 -0
  10. alle_proxy-0.1.0/docs/vpn-provider-research.md +66 -0
  11. alle_proxy-0.1.0/pyproject.toml +46 -0
  12. alle_proxy-0.1.0/scripts/gen_singbox_checksums.py +45 -0
  13. alle_proxy-0.1.0/src/alle/__init__.py +8 -0
  14. alle_proxy-0.1.0/src/alle/__main__.py +4 -0
  15. alle_proxy-0.1.0/src/alle/applog.py +73 -0
  16. alle_proxy-0.1.0/src/alle/cli.py +499 -0
  17. alle_proxy-0.1.0/src/alle/constants.py +28 -0
  18. alle_proxy-0.1.0/src/alle/credentials.py +84 -0
  19. alle_proxy-0.1.0/src/alle/daemon.py +193 -0
  20. alle_proxy-0.1.0/src/alle/engine.py +153 -0
  21. alle_proxy-0.1.0/src/alle/geo.py +45 -0
  22. alle_proxy-0.1.0/src/alle/locations.py +93 -0
  23. alle_proxy-0.1.0/src/alle/metrics.py +152 -0
  24. alle_proxy-0.1.0/src/alle/output.py +305 -0
  25. alle_proxy-0.1.0/src/alle/paths.py +16 -0
  26. alle_proxy-0.1.0/src/alle/probe.py +139 -0
  27. alle_proxy-0.1.0/src/alle/providers.py +301 -0
  28. alle_proxy-0.1.0/src/alle/reconnect.py +161 -0
  29. alle_proxy-0.1.0/src/alle/service.py +554 -0
  30. alle_proxy-0.1.0/src/alle/singbox.py +361 -0
  31. alle_proxy-0.1.0/src/alle/state.py +387 -0
  32. alle_proxy-0.1.0/src/alle/throughput.py +170 -0
  33. alle_proxy-0.1.0/src/alle/wgconf.py +173 -0
  34. alle_proxy-0.1.0/tests/conftest.py +16 -0
  35. alle_proxy-0.1.0/tests/test_auth_input.py +60 -0
  36. alle_proxy-0.1.0/tests/test_cli.py +295 -0
  37. alle_proxy-0.1.0/tests/test_credentials.py +38 -0
  38. alle_proxy-0.1.0/tests/test_daemon.py +47 -0
  39. alle_proxy-0.1.0/tests/test_engine.py +156 -0
  40. alle_proxy-0.1.0/tests/test_geo.py +30 -0
  41. alle_proxy-0.1.0/tests/test_locations.py +83 -0
  42. alle_proxy-0.1.0/tests/test_metrics.py +85 -0
  43. alle_proxy-0.1.0/tests/test_output.py +152 -0
  44. alle_proxy-0.1.0/tests/test_platform.py +53 -0
  45. alle_proxy-0.1.0/tests/test_probe.py +147 -0
  46. alle_proxy-0.1.0/tests/test_providers.py +244 -0
  47. alle_proxy-0.1.0/tests/test_reconnect.py +204 -0
  48. alle_proxy-0.1.0/tests/test_singbox.py +65 -0
  49. alle_proxy-0.1.0/tests/test_state.py +157 -0
  50. alle_proxy-0.1.0/tests/test_test.py +210 -0
  51. alle_proxy-0.1.0/tests/test_throughput.py +100 -0
  52. alle_proxy-0.1.0/tests/test_version.py +25 -0
  53. alle_proxy-0.1.0/tests/test_wgconf.py +99 -0
  54. alle_proxy-0.1.0/uv.lock +385 -0
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ lint:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v7
10
+ - uses: actions/setup-python@v6
11
+ with:
12
+ python-version: "3.14"
13
+ - uses: astral-sh/setup-uv@v8.2.0
14
+ with:
15
+ cache-suffix: lint-py3.14
16
+ - run: uv sync --all-extras
17
+ - name: Check lint
18
+ run: uv run ruff check
19
+
20
+ test:
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ matrix:
24
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
25
+ steps:
26
+ - uses: actions/checkout@v7
27
+ - name: Set up Python ${{ matrix.python-version }}
28
+ uses: actions/setup-python@v6
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+ - uses: astral-sh/setup-uv@v8.2.0
32
+ with:
33
+ cache-suffix: test-py${{ matrix.python-version }}
34
+ - run: uv sync --all-extras
35
+ - run: uv run pytest -q
@@ -0,0 +1,77 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v7
13
+
14
+ - uses: actions/setup-python@v6
15
+ with:
16
+ python-version: "3.14"
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v8.2.0
20
+ with:
21
+ cache-suffix: publish-py3.14
22
+
23
+ - name: Verify tag matches pyproject.toml version
24
+ run: |
25
+ tag="${GITHUB_REF_NAME#v}"
26
+ pkg=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
27
+ if [ "$tag" != "$pkg" ]; then
28
+ echo "::error::tag '$GITHUB_REF_NAME' (normalized '$tag') does not match pyproject.toml version '$pkg'"
29
+ exit 1
30
+ fi
31
+
32
+ - name: Build sdist + wheel
33
+ run: uv build
34
+
35
+ - name: Upload artifacts
36
+ uses: actions/upload-artifact@v7
37
+ with:
38
+ name: dist
39
+ path: dist/
40
+
41
+ publish-pypi:
42
+ needs: build
43
+ runs-on: ubuntu-latest
44
+ environment:
45
+ name: pypi
46
+ url: https://pypi.org/p/alle-proxy
47
+ permissions:
48
+ # Required for PyPI Trusted Publishing (OIDC).
49
+ # Configure the trusted publisher on PyPI before the first tag push.
50
+ id-token: write
51
+ steps:
52
+ - name: Download artifacts
53
+ uses: actions/download-artifact@v8
54
+ with:
55
+ name: dist
56
+ path: dist/
57
+
58
+ - name: Publish to PyPI
59
+ uses: pypa/gh-action-pypi-publish@release/v1
60
+
61
+ github-release:
62
+ needs: publish-pypi
63
+ runs-on: ubuntu-latest
64
+ permissions:
65
+ contents: write
66
+ steps:
67
+ - name: Download artifacts
68
+ uses: actions/download-artifact@v8
69
+ with:
70
+ name: dist
71
+ path: dist/
72
+
73
+ - name: Create GitHub Release
74
+ uses: softprops/action-gh-release@v3
75
+ with:
76
+ files: dist/*
77
+ generate_release_notes: true
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .venv/
7
+ .uv/
8
+ *.log
9
+ .ruff_cache/
10
+ .pytest_cache/
11
+ .coverage
12
+ coverage.xml
13
+ htmlcov/
14
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zydo and other alle contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,286 @@
1
+ Metadata-Version: 2.4
2
+ Name: alle-proxy
3
+ Version: 0.1.0
4
+ Summary: The unofficial, universal VPN client: manage multiple VPN providers and locations in one place through a single sing-box process, with rule-based routing planned. CLI today; web UI and menu-bar GUI planned.
5
+ Project-URL: Homepage, https://github.com/zydo/alle
6
+ Project-URL: Issues, https://github.com/zydo/alle/issues
7
+ Author: zydo
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: gateway,nordvpn,proxy,sing-box,vpn,wireguard
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: System :: Networking
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: pycountry>=23.12
18
+ Requires-Dist: pyyaml>=6.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ <p align="center">
22
+ <img src="assets/wordmark.svg" alt="alle" width="320">
23
+ </p>
24
+
25
+ <p align="center">
26
+ <a href="https://github.com/zydo/alle/actions/workflows/ci.yml"><img src="https://github.com/zydo/alle/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
27
+ <a href="https://pypi.org/project/alle-proxy/"><img src="https://img.shields.io/pypi/v/alle-proxy.svg" alt="PyPI"></a>
28
+ </p>
29
+
30
+ # alle
31
+
32
+ An unofficial but more powerful VPN client for multiple VPN providers.
33
+
34
+ # Why `alle`
35
+
36
+ Most VPN clients are built around one global idea: connect this device to a single
37
+ VPN server, then send everything through it until you disconnect or switch.
38
+
39
+ That is not enough when different resources need to appear from different regions
40
+ — a geo-fenced stream, a bank that blocks foreign IPs, a region-locked test
41
+ environment. Switching origins means disconnecting from one server and reconnecting
42
+ to another, and the official client on one machine usually cannot keep several
43
+ locations active at once anyway.
44
+
45
+ `alle` keeps multiple VPN exits live at the same time, from one provider or mixed
46
+ across several. Say you want a US exit, a UK exit, and a Japan exit at once —
47
+ NordVPN for the US and Japan, ProtonVPN for the UK:
48
+
49
+ ```text
50
+ streaming + admin ──► `alle` ──► United States (NordVPN)
51
+ test runner ──► `alle` ──► Japan (NordVPN)
52
+ bank login ──► `alle` ──► United Kingdom (Proton VPN)
53
+ ```
54
+
55
+ Each app points at the exit it needs; they run concurrently and independently, so
56
+ opening the bank never disturbs the stream.
57
+
58
+ In short: not one global location you keep switching, but several exits alive at
59
+ once, each used where it is needed.
60
+
61
+ ## What `alle` does
62
+
63
+ `alle` runs multiple VPN exits side by side. Each exit is exposed as its own
64
+ local HTTP+SOCKS proxy on `127.0.0.1:<port>`. A planned single HTTP+SOCKS entry
65
+ point will route traffic by rule to a VPN exit or to direct outbound with no
66
+ proxy. Instead of changing your whole machine's VPN location, you point each app,
67
+ browser profile, script, or test job at the path it needs.
68
+
69
+ Under the hood, `alle` manages one
70
+ [`sing-box`](https://github.com/SagerNet/sing-box) process. Each channel becomes
71
+ one local proxy inbound routed through one WireGuard VPN peer. Channels can come
72
+ from different providers, so a NordVPN exit and a Proton VPN `.conf` import can
73
+ run at the same time.
74
+
75
+ ## Current status
76
+
77
+ `alle` is usable today as a CLI-first client for per-app/per-workflow VPN exits.
78
+
79
+ **Providers**
80
+
81
+ | Provider | Support |
82
+ | ---------- | ------------------------------------------------------------------------- |
83
+ | NordVPN | Token/API setup, location selection, automatic WireGuard channel creation |
84
+ | Proton VPN | WireGuard `.conf` import |
85
+
86
+ **Platforms**
87
+
88
+ | Platform | Support |
89
+ | -------- | --------- |
90
+ | macOS | Supported |
91
+ | Linux | Supported |
92
+ | Windows | Planned |
93
+
94
+ **Features**
95
+
96
+ | Phase | Status |
97
+ | ----------------- | ---------------------------------------------------------------------- |
98
+ | Core CLI | Providers, channels, per-channel proxies, status, tests, logs, metrics |
99
+ | Routing | Planned |
100
+ | Web UI | Planned |
101
+ | Desktop companion | Planned |
102
+ | Distribution | PyPI CLI package; native installers planned |
103
+
104
+ ## Install
105
+
106
+ `alle` requires Python 3.10 or newer.
107
+
108
+ With `pip`:
109
+
110
+ ```bash
111
+ python -m pip install alle-proxy
112
+ ```
113
+
114
+ With `pipx`:
115
+
116
+ ```bash
117
+ pipx install alle-proxy
118
+ ```
119
+
120
+ With `uv` as an installed tool:
121
+
122
+ ```bash
123
+ uv tool install alle-proxy
124
+ ```
125
+
126
+ Or run it directly with `uvx`:
127
+
128
+ ```bash
129
+ uvx --from alle-proxy alle --help
130
+ ```
131
+
132
+ After installation:
133
+
134
+ ```bash
135
+ alle version
136
+ alle --help
137
+ ```
138
+
139
+ ## Quick start
140
+
141
+ Add a provider, create a channel, start the runtime, then use the channel's local
142
+ proxy port.
143
+
144
+ ```bash
145
+ alle providers add nordvpn
146
+ alle channels add nordvpn --country "United States"
147
+ alle start
148
+ alle channels ls
149
+ ```
150
+
151
+ `alle channels ls` prints the local proxy port for each channel:
152
+
153
+ ```text
154
+ PROVIDER NAME PORT COUNTRY CITY
155
+ -------- --------------- ------ ------------- ----------
156
+ NordVPN united_states_1 :53124 United States (Any City)
157
+ ```
158
+
159
+ Use that port from any tool or app that supports an HTTP or SOCKS proxy:
160
+
161
+ ```bash
162
+ curl -x http://127.0.0.1:53124 https://api.ipify.org
163
+ ```
164
+
165
+ Check health and traffic:
166
+
167
+ ```bash
168
+ alle status
169
+ alle test
170
+ alle metrics
171
+ ```
172
+
173
+ **Provider setup**
174
+
175
+ `alle` supports two provider setup styles today:
176
+
177
+ **NordVPN** uses an access token:
178
+
179
+ ```bash
180
+ alle providers add nordvpn
181
+ alle locations nordvpn
182
+ alle locations nordvpn --country "United States"
183
+ alle channels add nordvpn --country "United States" --city "Seattle"
184
+ ```
185
+
186
+ **Proton VPN** uses WireGuard config files downloaded from Proton:
187
+
188
+ ```bash
189
+ alle providers add protonvpn
190
+ alle channels add protonvpn --config ~/Downloads/wg-US-CA-842.conf
191
+ ```
192
+
193
+ Re-importing the same `.conf` file updates that channel in place, keeping the
194
+ same channel id and local port.
195
+
196
+ **Common commands**
197
+
198
+ Useful commands after setup:
199
+
200
+ ```bash
201
+ alle providers ls
202
+ alle channels ls
203
+ alle status
204
+ alle test
205
+ alle metrics
206
+ alle logs
207
+ alle stop
208
+ ```
209
+
210
+ Most read commands support `--json` for scripts:
211
+
212
+ ```bash
213
+ alle status --json
214
+ alle channels ls --json
215
+ alle metrics --json
216
+ ```
217
+
218
+ For the complete command reference, see the
219
+ [CLI Reference](docs/cli-reference.md).
220
+
221
+ ## Rule-based routing
222
+
223
+ To be implemented.
224
+
225
+ ## How it works
226
+
227
+ - `alle` keeps its local state under `~/.alle/`, or under `$ALLE_HOME` when that
228
+ environment variable is set. This includes providers, channels, credentials,
229
+ metrics, generated config, logs, and runtime files.
230
+
231
+ - `alle` manages one [`sing-box`](https://github.com/SagerNet/sing-box) process
232
+ instead of starting one VPN process per channel. The generated config contains
233
+ one local HTTP+SOCKS inbound per channel.
234
+
235
+ - Each channel routes to one WireGuard peer. NordVPN channels are created from
236
+ the provider API; Proton VPN channels are created by importing a WireGuard
237
+ `.conf` file. After creation, both behave the same way.
238
+
239
+ - WireGuard is connectionless, so `alle` does not model channels as connected or
240
+ disconnected. A channel exists in config; its health comes from the latest
241
+ probe.
242
+
243
+ - Local proxy ports are assigned by the OS and stored in state. Use
244
+ `alle channels ls` to see the current ports.
245
+
246
+ - The background runtime applies state changes, keeps the `sing-box` process in
247
+ sync, probes channel health, and records per-channel traffic totals.
248
+
249
+ - `alle` uses a pinned upstream `sing-box` release and verifies its checksum
250
+ before running it.
251
+
252
+ ## Security and privacy
253
+
254
+ - Provider credentials and WireGuard private keys are stored locally under
255
+ `~/.alle/` or `$ALLE_HOME`.
256
+ - Credential and state files are written with private file permissions where
257
+ supported by the OS.
258
+ - `alle` does not read provider tokens from environment variables; credentials
259
+ are added explicitly with `alle providers add`.
260
+ - `alle` downloads a pinned upstream `sing-box` release and verifies its checksum
261
+ before running it.
262
+ - Local proxy ports bind to loopback. Traffic only uses a VPN exit when an app is
263
+ pointed at one of those proxies.
264
+
265
+ ## Roadmap and non-goals
266
+
267
+ Planned next steps:
268
+
269
+ - Rule-based routing through a single local HTTP+SOCKS entry point.
270
+ - More WireGuard-capable VPN providers. See
271
+ [VPN Provider Research](docs/vpn-provider-research.md).
272
+ - Web UI for managing channels and routing rules.
273
+ - Desktop companion with OS-level VPN integration.
274
+ - Windows support and broader distribution.
275
+
276
+ Non-goals:
277
+
278
+ - OpenVPN or IKEv2/IPsec support.
279
+ - VPN providers without usable WireGuard support, such as ExpressVPN, HideMyAss,
280
+ Perfect Privacy, Privado, SlickVPN, VPN.ac/VPNSecure, and Giganews.
281
+ - SOCKS5-only or unencrypted proxy providers.
282
+ - Bundling `sing-box` inside the Python package.
283
+
284
+ ## License
285
+
286
+ MIT
@@ -0,0 +1,266 @@
1
+ <p align="center">
2
+ <img src="assets/wordmark.svg" alt="alle" width="320">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/zydo/alle/actions/workflows/ci.yml"><img src="https://github.com/zydo/alle/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
7
+ <a href="https://pypi.org/project/alle-proxy/"><img src="https://img.shields.io/pypi/v/alle-proxy.svg" alt="PyPI"></a>
8
+ </p>
9
+
10
+ # alle
11
+
12
+ An unofficial but more powerful VPN client for multiple VPN providers.
13
+
14
+ # Why `alle`
15
+
16
+ Most VPN clients are built around one global idea: connect this device to a single
17
+ VPN server, then send everything through it until you disconnect or switch.
18
+
19
+ That is not enough when different resources need to appear from different regions
20
+ — a geo-fenced stream, a bank that blocks foreign IPs, a region-locked test
21
+ environment. Switching origins means disconnecting from one server and reconnecting
22
+ to another, and the official client on one machine usually cannot keep several
23
+ locations active at once anyway.
24
+
25
+ `alle` keeps multiple VPN exits live at the same time, from one provider or mixed
26
+ across several. Say you want a US exit, a UK exit, and a Japan exit at once —
27
+ NordVPN for the US and Japan, ProtonVPN for the UK:
28
+
29
+ ```text
30
+ streaming + admin ──► `alle` ──► United States (NordVPN)
31
+ test runner ──► `alle` ──► Japan (NordVPN)
32
+ bank login ──► `alle` ──► United Kingdom (Proton VPN)
33
+ ```
34
+
35
+ Each app points at the exit it needs; they run concurrently and independently, so
36
+ opening the bank never disturbs the stream.
37
+
38
+ In short: not one global location you keep switching, but several exits alive at
39
+ once, each used where it is needed.
40
+
41
+ ## What `alle` does
42
+
43
+ `alle` runs multiple VPN exits side by side. Each exit is exposed as its own
44
+ local HTTP+SOCKS proxy on `127.0.0.1:<port>`. A planned single HTTP+SOCKS entry
45
+ point will route traffic by rule to a VPN exit or to direct outbound with no
46
+ proxy. Instead of changing your whole machine's VPN location, you point each app,
47
+ browser profile, script, or test job at the path it needs.
48
+
49
+ Under the hood, `alle` manages one
50
+ [`sing-box`](https://github.com/SagerNet/sing-box) process. Each channel becomes
51
+ one local proxy inbound routed through one WireGuard VPN peer. Channels can come
52
+ from different providers, so a NordVPN exit and a Proton VPN `.conf` import can
53
+ run at the same time.
54
+
55
+ ## Current status
56
+
57
+ `alle` is usable today as a CLI-first client for per-app/per-workflow VPN exits.
58
+
59
+ **Providers**
60
+
61
+ | Provider | Support |
62
+ | ---------- | ------------------------------------------------------------------------- |
63
+ | NordVPN | Token/API setup, location selection, automatic WireGuard channel creation |
64
+ | Proton VPN | WireGuard `.conf` import |
65
+
66
+ **Platforms**
67
+
68
+ | Platform | Support |
69
+ | -------- | --------- |
70
+ | macOS | Supported |
71
+ | Linux | Supported |
72
+ | Windows | Planned |
73
+
74
+ **Features**
75
+
76
+ | Phase | Status |
77
+ | ----------------- | ---------------------------------------------------------------------- |
78
+ | Core CLI | Providers, channels, per-channel proxies, status, tests, logs, metrics |
79
+ | Routing | Planned |
80
+ | Web UI | Planned |
81
+ | Desktop companion | Planned |
82
+ | Distribution | PyPI CLI package; native installers planned |
83
+
84
+ ## Install
85
+
86
+ `alle` requires Python 3.10 or newer.
87
+
88
+ With `pip`:
89
+
90
+ ```bash
91
+ python -m pip install alle-proxy
92
+ ```
93
+
94
+ With `pipx`:
95
+
96
+ ```bash
97
+ pipx install alle-proxy
98
+ ```
99
+
100
+ With `uv` as an installed tool:
101
+
102
+ ```bash
103
+ uv tool install alle-proxy
104
+ ```
105
+
106
+ Or run it directly with `uvx`:
107
+
108
+ ```bash
109
+ uvx --from alle-proxy alle --help
110
+ ```
111
+
112
+ After installation:
113
+
114
+ ```bash
115
+ alle version
116
+ alle --help
117
+ ```
118
+
119
+ ## Quick start
120
+
121
+ Add a provider, create a channel, start the runtime, then use the channel's local
122
+ proxy port.
123
+
124
+ ```bash
125
+ alle providers add nordvpn
126
+ alle channels add nordvpn --country "United States"
127
+ alle start
128
+ alle channels ls
129
+ ```
130
+
131
+ `alle channels ls` prints the local proxy port for each channel:
132
+
133
+ ```text
134
+ PROVIDER NAME PORT COUNTRY CITY
135
+ -------- --------------- ------ ------------- ----------
136
+ NordVPN united_states_1 :53124 United States (Any City)
137
+ ```
138
+
139
+ Use that port from any tool or app that supports an HTTP or SOCKS proxy:
140
+
141
+ ```bash
142
+ curl -x http://127.0.0.1:53124 https://api.ipify.org
143
+ ```
144
+
145
+ Check health and traffic:
146
+
147
+ ```bash
148
+ alle status
149
+ alle test
150
+ alle metrics
151
+ ```
152
+
153
+ **Provider setup**
154
+
155
+ `alle` supports two provider setup styles today:
156
+
157
+ **NordVPN** uses an access token:
158
+
159
+ ```bash
160
+ alle providers add nordvpn
161
+ alle locations nordvpn
162
+ alle locations nordvpn --country "United States"
163
+ alle channels add nordvpn --country "United States" --city "Seattle"
164
+ ```
165
+
166
+ **Proton VPN** uses WireGuard config files downloaded from Proton:
167
+
168
+ ```bash
169
+ alle providers add protonvpn
170
+ alle channels add protonvpn --config ~/Downloads/wg-US-CA-842.conf
171
+ ```
172
+
173
+ Re-importing the same `.conf` file updates that channel in place, keeping the
174
+ same channel id and local port.
175
+
176
+ **Common commands**
177
+
178
+ Useful commands after setup:
179
+
180
+ ```bash
181
+ alle providers ls
182
+ alle channels ls
183
+ alle status
184
+ alle test
185
+ alle metrics
186
+ alle logs
187
+ alle stop
188
+ ```
189
+
190
+ Most read commands support `--json` for scripts:
191
+
192
+ ```bash
193
+ alle status --json
194
+ alle channels ls --json
195
+ alle metrics --json
196
+ ```
197
+
198
+ For the complete command reference, see the
199
+ [CLI Reference](docs/cli-reference.md).
200
+
201
+ ## Rule-based routing
202
+
203
+ To be implemented.
204
+
205
+ ## How it works
206
+
207
+ - `alle` keeps its local state under `~/.alle/`, or under `$ALLE_HOME` when that
208
+ environment variable is set. This includes providers, channels, credentials,
209
+ metrics, generated config, logs, and runtime files.
210
+
211
+ - `alle` manages one [`sing-box`](https://github.com/SagerNet/sing-box) process
212
+ instead of starting one VPN process per channel. The generated config contains
213
+ one local HTTP+SOCKS inbound per channel.
214
+
215
+ - Each channel routes to one WireGuard peer. NordVPN channels are created from
216
+ the provider API; Proton VPN channels are created by importing a WireGuard
217
+ `.conf` file. After creation, both behave the same way.
218
+
219
+ - WireGuard is connectionless, so `alle` does not model channels as connected or
220
+ disconnected. A channel exists in config; its health comes from the latest
221
+ probe.
222
+
223
+ - Local proxy ports are assigned by the OS and stored in state. Use
224
+ `alle channels ls` to see the current ports.
225
+
226
+ - The background runtime applies state changes, keeps the `sing-box` process in
227
+ sync, probes channel health, and records per-channel traffic totals.
228
+
229
+ - `alle` uses a pinned upstream `sing-box` release and verifies its checksum
230
+ before running it.
231
+
232
+ ## Security and privacy
233
+
234
+ - Provider credentials and WireGuard private keys are stored locally under
235
+ `~/.alle/` or `$ALLE_HOME`.
236
+ - Credential and state files are written with private file permissions where
237
+ supported by the OS.
238
+ - `alle` does not read provider tokens from environment variables; credentials
239
+ are added explicitly with `alle providers add`.
240
+ - `alle` downloads a pinned upstream `sing-box` release and verifies its checksum
241
+ before running it.
242
+ - Local proxy ports bind to loopback. Traffic only uses a VPN exit when an app is
243
+ pointed at one of those proxies.
244
+
245
+ ## Roadmap and non-goals
246
+
247
+ Planned next steps:
248
+
249
+ - Rule-based routing through a single local HTTP+SOCKS entry point.
250
+ - More WireGuard-capable VPN providers. See
251
+ [VPN Provider Research](docs/vpn-provider-research.md).
252
+ - Web UI for managing channels and routing rules.
253
+ - Desktop companion with OS-level VPN integration.
254
+ - Windows support and broader distribution.
255
+
256
+ Non-goals:
257
+
258
+ - OpenVPN or IKEv2/IPsec support.
259
+ - VPN providers without usable WireGuard support, such as ExpressVPN, HideMyAss,
260
+ Perfect Privacy, Privado, SlickVPN, VPN.ac/VPNSecure, and Giganews.
261
+ - SOCKS5-only or unencrypted proxy providers.
262
+ - Bundling `sing-box` inside the Python package.
263
+
264
+ ## License
265
+
266
+ MIT