psiphon 2.0.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.
psiphon-2.0.0/LICENSE ADDED
@@ -0,0 +1,92 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: KpihX (Ivann Harold KAMDEM POUOKAM)
6
+ Licensed Work: psiphon 2.0.0
7
+ The Licensed Work is (c) 2026 KpihX (Ivann Harold KAMDEM POUOKAM).
8
+
9
+ Additional Use Grant:
10
+ You may make production use of the Licensed Work solely for personal,
11
+ noncommercial purposes or for noncommercial teaching and academic research.
12
+ This additional permission does not authorize commercial production use,
13
+ including operating a commercial product or service, providing paid production
14
+ services to third parties, or using the Licensed Work in revenue-generating
15
+ business operations. For production use outside this grant, contact the
16
+ Licensor at kapoivha@gmail.com for a separate commercial license.
17
+ This additional grant does not restrict the non-production rights granted
18
+ under the Terms, including commercial evaluation, prototyping and testing.
19
+
20
+ Change Date: 2030-10-24
21
+ Change License: GNU Affero General Public License version 3 only
22
+ (SPDX: AGPL-3.0-only)
23
+
24
+ License text copyright © 2017 MariaDB Corporation Ab, All Rights Reserved.
25
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
26
+
27
+ Terms
28
+
29
+ The Licensor hereby grants you the right to copy, modify, create derivative
30
+ works, redistribute, and make non-production use of the Licensed Work. The
31
+ Licensor may make an Additional Use Grant, above, permitting limited
32
+ production use.
33
+
34
+ Effective on the Change Date, or the fourth anniversary of the first publicly
35
+ available distribution of a specific version of the Licensed Work under this
36
+ License, whichever comes first, the Licensor hereby grants you rights under
37
+ the terms of the Change License, and the rights granted in the paragraph
38
+ above terminate.
39
+
40
+ If your use of the Licensed Work does not comply with the requirements
41
+ currently in effect as described in this License, you must purchase a
42
+ commercial license from the Licensor, its affiliated entities, or authorized
43
+ resellers, or you must refrain from using the Licensed Work.
44
+
45
+ All copies of the original and modified Licensed Work, and derivative works
46
+ of the Licensed Work, are subject to this License. This License applies
47
+ separately for each version of the Licensed Work and the Change Date may vary
48
+ for each version of the Licensed Work released by Licensor.
49
+
50
+ You must conspicuously display this License on each original or modified copy
51
+ of the Licensed Work. If you receive the Licensed Work in original or
52
+ modified form from a third party, the terms and conditions set forth in this
53
+ License apply to your use of that work.
54
+
55
+ Any use of the Licensed Work in violation of this License will automatically
56
+ terminate your rights under this License for the current and all other
57
+ versions of the Licensed Work.
58
+
59
+ This License does not grant you any right in any trademark or logo of
60
+ Licensor or its affiliates (provided that you may use a trademark or logo of
61
+ Licensor as expressly required by this License).
62
+
63
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
64
+ AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
65
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
67
+ TITLE.
68
+
69
+ MariaDB hereby grants you permission to use this License’s text to license
70
+ your works, and to refer to it using the trademark “Business Source License”,
71
+ as long as you comply with the Covenants of Licensor below.
72
+
73
+ Covenants of Licensor
74
+
75
+ In consideration of the right to use this License’s text and the “Business
76
+ Source License” name and trademark, Licensor covenants to MariaDB, and to all
77
+ other recipients of the licensed work to be provided by Licensor:
78
+
79
+ 1. To specify as the Change License the GPL Version 2.0 or any later version,
80
+ or a license that is compatible with GPL Version 2.0 or a later version,
81
+ where “compatible” means that software provided under the Change License can
82
+ be included in a program with software provided under GPL Version 2.0 or a
83
+ later version. Licensor may specify additional Change Licenses without
84
+ limitation.
85
+
86
+ 2. To either: (a) specify an additional grant of rights to use that does not
87
+ impose any additional restriction on the right granted in this License, as
88
+ the Additional Use Grant; or (b) insert the text “None”.
89
+
90
+ 3. To specify a Change Date.
91
+
92
+ 4. Not to modify this License in any other way.
psiphon-2.0.0/PKG-INFO ADDED
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.4
2
+ Name: psiphon
3
+ Version: 2.0.0
4
+ Summary: Foreground native Psiphon packet tunnel and explicit local proxy for Linux
5
+ Author: KpihX
6
+ Author-email: KpihX <kapoivha@gmail.com>
7
+ License-Expression: BUSL-1.1
8
+ License-File: LICENSE
9
+ Requires-Dist: requests>=2.34.2
10
+ Requires-Dist: rich>=15.0.0
11
+ Requires-Dist: typer>=0.27.3
12
+ Requires-Python: >=3.12
13
+ Project-URL: Repository, https://github.com/kpihx-labs/psiphon
14
+ Project-URL: GitLab, https://gitlab.com/kpihx-labs/vpns/psiphon
15
+ Project-URL: Issues, https://github.com/kpihx-labs/psiphon/issues
16
+ Description-Content-Type: text/markdown
17
+
18
+ # psiphon
19
+
20
+ Independent Linux x86-64 CLI around the unchanged official Psiphon ConsoleClient.
21
+ Version 2 runs in the foreground: native packet tunnel by default, explicit local
22
+ HTTP/SOCKS proxy with `--proxy/-p`. Native connect automatically requests sudo
23
+ for its network supervisor only. No systemd service or root shell is required.
24
+
25
+ ## Install and setup
26
+
27
+ ```bash
28
+ sudo apt install iproute2 systemd-resolved curl sudo
29
+ uv tool install psiphon
30
+ psiphon setup # explicit network download, no route changes
31
+ psiphon doctor # offline engine/hash and native prerequisite checks
32
+ ```
33
+
34
+ First-time dependency retrieval can use `uv sync` explicitly. `make uv-link` is
35
+ available for editable development. No engine is bundled in the wheel. Setup
36
+ downloads only the pinned official artifact, checks SHA-256 before installation,
37
+ then atomically installs it in `$XDG_DATA_HOME/psiphon/<revision>/` (default
38
+ `$HOME/.local/share`). A valid existing engine requires no download. Failed hashes
39
+ leave the existing binary untouched. Only Linux x86-64 is supported by this pin.
40
+
41
+ The artifact repository revision is
42
+ `33ff79e89260f3adf92e846b96b3369b86a819d8`, with SHA-256
43
+ `046ca168da25d2c0e61c1c15e2b73bfbb67f5208112a4c94fe58fc5aab784f19`.
44
+ Its official commit describes source revision `c4a09096ab401b57f52e092c8f2f829c8fac7628`.
45
+ That metadata is provenance, not a reproducible-build or signed-release guarantee.
46
+ The pin and URLs live in `src/psiphon/config.py`; no moving-master downloads occur.
47
+
48
+ ## Foreground use
49
+
50
+ ```bash
51
+ # No root and no OS route/DNS changes. Leave this terminal running:
52
+ psiphon connect -p -r FR
53
+ # In another terminal, explicitly measure the local proxy:
54
+ psiphon status -p -g --speed -n 3
55
+ curl --proxy socks5h://127.0.0.1:11081 https://api.ipify.org
56
+
57
+ # Native mode, from your normal shell, using the SAME user setup and XDG state:
58
+ psiphon doctor # read-only; does not ask sudo
59
+ psiphon connect -r DE # requests sudo automatically if preflight passes
60
+ # Optional -i eth0 selects the bypass interface.
61
+ ```
62
+
63
+ Setup, doctor, controller, configuration, logs and datastore remain under your
64
+ normal user. There is no root setup or root XDG copy. After read-only preflight,
65
+ connect authenticates with `sudo -v` at the terminal, then launches only the network
66
+ supervisor through noninteractive sudo. Password input is never read from the
67
+ controller/supervisor JSON pipe. If elevation policy replaces that pipe with a TTY,
68
+ the worker refuses before mutations. Cancellation starts no supervisor or route change.
69
+ For agent-operated privilege, launch connect only in an armed tmux pane in the
70
+ agent's current window. No sudo or privileged operation runs in offline checks.
71
+
72
+ **Escalation trust:** sudo approves this local Python interpreter and installed
73
+ package, including an editable checkout if that is what you installed. They may
74
+ be user-writable. `-I` avoids ambient PYTHONPATH/user-site, but does not make that
75
+ code, its environment or its dependencies a tamper-resistant root helper. This is
76
+ trusted local-install escalation, equivalent in trust to running a local script
77
+ through sudo, not a boundary against a compromised account/installation. Prefer a
78
+ reviewed wheel installation; do not add a passwordless sudoers rule for the helper.
79
+
80
+ The elevated worker accepts fixed native operations, not arbitrary command arrays.
81
+ It independently checks engine ownership, path components and the pinned SHA-256,
82
+ then executes a sealed memfd snapshot, preventing executable replacement after the
83
+ hash check. Symlinked engine paths and group/world-writable engine paths/ancestors
84
+ are refused. A local libc memfd API is used if the Python build omits its wrapper.
85
+ The TUN is assigned to the caller UID. The engine runs with the caller UID/GID,
86
+ no supplementary groups, and only its verified descriptor inherited besides
87
+ standard I/O; it reads the
88
+ user configuration and writes the user datastore. Root does not parse the user's
89
+ engine configuration or create root-owned XDG state. Modern Linux permits an
90
+ initial unprivileged SO_BINDTODEVICE; a local socket-only preflight rejects hosts
91
+ where this fails. There is no fallback to a root engine.
92
+
93
+ Region selection is optional: without `-r`, the engine chooses. A requested region
94
+ must match its fresh reported region before readiness succeeds. Ctrl-C stops the
95
+ session and rolls back. `disconnect` requests termination of the owned controller
96
+ in the same user/XDG context; it checks PID start time and exact command identity.
97
+ There is no detached mode. HTTP is `127.0.0.1:18081`, SOCKS is `127.0.0.1:11081`.
98
+ Busy ports are not stolen. Native mode disables both local proxies.
99
+
100
+ ## Native routing and limits
101
+
102
+ ConsoleClient opens a **preexisting named TUN** using `-tunDevice`, binds its
103
+ transport sockets to the uplink with `-tunBindInterface`, and uses
104
+ `-tunDNSServers` for bypass/bootstrap DNS. The wrapper does not use tun2socks.
105
+
106
+ The session supervisor creates only its own TUN, policy table and rules:
107
+
108
+ - IPv4 routes through `psiphon0` in table 18081, priority 101. Priority 100 permits
109
+ explicitly uplink-bound IPv4 sockets to use the unchanged main table. This is
110
+ the engine's escape path, not an application-isolation/security boundary.
111
+ - Local-address routing remains local. Other IPv4 traffic, including LAN traffic,
112
+ goes to the TUN. IPv6 nonlocal traffic is temporarily unreachable; this wrapper
113
+ does not claim tested native IPv6 support. No global IPv6/sysctl switch changes.
114
+ - Reverse-path filtering changes to loose mode only on the uplink and owned TUN;
115
+ previous per-link values are restored. Existing main routes remain untouched.
116
+ - DNS uses upstream's transparent IPv4 resolver **10.0.0.2**, with `resolvectl dns`
117
+ and routing domain `~.` on the owned link. Upstream also defines transparent
118
+ IPv6 DNS `fd19:ca83:e6d5:1c44::2`, unused by this IPv4-only policy.
119
+ - Native preflight requires the systemd-resolved stub, free session resources
120
+ and compatible policy rules. Per-link DNS domains are saved, temporarily cleared
121
+ and restored exactly. Global domains remain configured; their resolver traffic
122
+ also follows the TUN. Private campus/Tailscale names can be unavailable during
123
+ the session. No persistent DNS configuration or resolv.conf is rewritten.
124
+
125
+ The native survey on 2026-10-24 exercised 31 codes on the workstation with
126
+ Tailscale/campus DNS present. Sixteen produced verified packet-tunnel downloads;
127
+ all sessions restored the original routes and per-link DNS domains. See
128
+ `BENCHMARKS.md` for failures and qualifications.
129
+
130
+ Each successful mutation has an inverse. The supervisor owns the engine and
131
+ observes a private pipe: normal exit, startup failure, SIGTERM, Ctrl-C or controller
132
+ SIGKILL closes it, stops/reaps the engine, and rolls back in reverse order. Cleanup
133
+ errors are explicit, never ignored. This is not protection against the supervisor
134
+ itself being killed, host failure, an administrator changing resources mid-session,
135
+ new DNS configuration appearing mid-session, or applications deliberately binding
136
+ to the bypass interface. It is not a permanent kill switch. Ordinary connectivity
137
+ returns after rollback. Do not use this as an anonymity or comprehensive leak-proof
138
+ security boundary. The live validation is scoped to the tested host and fleet.
139
+
140
+ Not every proxy exit supports the packet-tunnel SSH channel. Native readiness
141
+ requires a fresh tunnel notice, matching region, the channel-established diagnostic,
142
+ the active TUN route, and a real HTTPS public-IP response over that route. A proxy
143
+ success is never substituted. Missing packet support times out and rolls back;
144
+ the engine selects/retries its servers. The wait budget bounds readiness polling,
145
+ not OS hostname-resolution stalls. Availability and traffic rules remain fleet-controlled.
146
+
147
+ Official source references, at the pinned source revision:
148
+
149
+ - [ConsoleClient flags and device binding](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c4a09096ab401b57f52e092c8f2f829c8fac7628/ConsoleClient/main.go)
150
+ - [Transparent DNS and packet rewriting](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c4a09096ab401b57f52e092c8f2f829c8fac7628/psiphon/common/tun/tun.go)
151
+ - [Packet SSH channel establishment](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c4a09096ab401b57f52e092c8f2f829c8fac7628/psiphon/tunnel.go)
152
+
153
+ ## Status and explicit benchmarks
154
+
155
+ ```bash
156
+ psiphon status # current OS route + HTTPS IP, with or without Psiphon
157
+ psiphon status -g # opt-in third-party country/city lookup
158
+ psiphon status --speed -n 3 # explicit download and upload on the current route
159
+ psiphon status -p -g -d # explicit local proxy instead
160
+ psiphon regions # catalogue + last observed fleet notice
161
+ psiphon bench -r FR -r JP -p # selected proxy regions, no OS route changes
162
+ psiphon bench -a -p # explicit all-region proxy survey
163
+ psiphon bench -a # explicit native survey; sudo supervisor per session
164
+ ```
165
+
166
+ All custom CLI options have short aliases; help is `-h/--help`, version is `-v`.
167
+ Status does not depend on a saved Psiphon PID and never silently chooses the proxy.
168
+ Ambient proxy and NO_PROXY settings are ignored. GeoIP is a database observation,
169
+ not proof of physical location; opting in discloses the measured IP to ipwho.is.
170
+ IP queries and speed tests also contact external services.
171
+
172
+ Downloads stream real payload. Uploads stream at most 40 MB per sample and require
173
+ a successful HTTP response, not just locally queued bytes. Timing includes setup
174
+ and response overhead; `-s` is a measurement/send budget, not an absolute deadline.
175
+ Errors remain errors, never zero-Mbps results. Endpoints live in `config.py`.
176
+
177
+ `bench` requires either `-a` or repeated `-r`. All-region scope is the catalogue
178
+ union observed fleet codes, extended when later notices announce additional codes;
179
+ it is not a claim to discover every server or every future region. Each region is
180
+ attempted once. Private JSONL records errors and measurements, then exact selected
181
+ and attempted sets. It does not run from ordinary connect/status or offline checks.
182
+ Historical proxy measurements remain in [BENCHMARKS.md](BENCHMARKS.md); they do not
183
+ validate the new native path. No standalone benchmark scripts are used.
184
+
185
+ ## State, packaging and development
186
+
187
+ | Data | Default location |
188
+ |---|---|
189
+ | Packaged defaults and verbatim engine license | `src/psiphon/assets/` (also in wheel) |
190
+ | Verified engine and its separate GPL license | `$XDG_DATA_HOME/psiphon/<revision>/` |
191
+ | Active engine configuration | `$XDG_CONFIG_HOME/psiphon/psiphon.config` |
192
+ | Engine server lists/datastore | `$XDG_CACHE_HOME/psiphon/core/` |
193
+ | Explicit benchmark observations | `$XDG_CACHE_HOME/psiphon/bench-<UTC>.jsonl` |
194
+ | Append-only diagnostic log | `$XDG_STATE_HOME/psiphon/psiphon.log` |
195
+ | Controller lock and identity | `$XDG_RUNTIME_DIR/psiphon-<uid>/`, or `/tmp/psiphon-<uid>/` |
196
+
197
+ Standard XDG fallbacks apply. Directories and runtime files are private. Logs and
198
+ benchmarks can contain IPs and diagnostic URLs; do not publish them raw. No account
199
+ or password is stored. Old service assets and management code are removed; upgrading
200
+ does not stop or remove a previously installed OS service automatically. Such a unit
201
+ must be explicitly stopped/disabled before migration; this offline pass does not
202
+ change the installed system or global tool.
203
+
204
+ `make check` synchronizes from the frozen lock **offline**, builds the wheel,
205
+ runs Ruff, compilation, Pyright, offline behavior/failure-injection tests and an
206
+ isolated installed-wheel resource/CLI test. Real pipe EOF/SIGKILL tests use dummy
207
+ processes and temporary files, never a VPN or privileged route command.
208
+
209
+ `make build` builds locally. `make publish` is an explicit remote action and must
210
+ run in an approved `with-env` tmux pane. `make push` uses the current branch and
211
+ both SSH remotes `github` and `gitlab`, failing on errors. Neither is run by checks.
212
+
213
+ ## License and trust
214
+
215
+ Only the original Python wrapper is BUSL 1.1, with the personal/noncommercial
216
+ academic grant in [LICENSE](LICENSE), changing to AGPL-3.0-only no later than
217
+ 2030-10-24. Its legal body is canonical and does not relicense the engine.
218
+
219
+ The external engine remains upstream GPLv3 (source notices specify version 3 or
220
+ later). The exact pinned upstream license is shipped separately at
221
+ `src/psiphon/assets/LICENSE.engine` and copied beside the engine by setup. The wheel
222
+ contains no engine binary. Anyone redistributing the engine must separately meet
223
+ upstream GPL and corresponding-source obligations; the wrapper license grants no
224
+ rights to Psiphon's trademarks or service. Psiphon operates the exit fleet, not
225
+ this wrapper. Neither universal DPI evasion nor anonymity is guaranteed.
@@ -0,0 +1,208 @@
1
+ # psiphon
2
+
3
+ Independent Linux x86-64 CLI around the unchanged official Psiphon ConsoleClient.
4
+ Version 2 runs in the foreground: native packet tunnel by default, explicit local
5
+ HTTP/SOCKS proxy with `--proxy/-p`. Native connect automatically requests sudo
6
+ for its network supervisor only. No systemd service or root shell is required.
7
+
8
+ ## Install and setup
9
+
10
+ ```bash
11
+ sudo apt install iproute2 systemd-resolved curl sudo
12
+ uv tool install psiphon
13
+ psiphon setup # explicit network download, no route changes
14
+ psiphon doctor # offline engine/hash and native prerequisite checks
15
+ ```
16
+
17
+ First-time dependency retrieval can use `uv sync` explicitly. `make uv-link` is
18
+ available for editable development. No engine is bundled in the wheel. Setup
19
+ downloads only the pinned official artifact, checks SHA-256 before installation,
20
+ then atomically installs it in `$XDG_DATA_HOME/psiphon/<revision>/` (default
21
+ `$HOME/.local/share`). A valid existing engine requires no download. Failed hashes
22
+ leave the existing binary untouched. Only Linux x86-64 is supported by this pin.
23
+
24
+ The artifact repository revision is
25
+ `33ff79e89260f3adf92e846b96b3369b86a819d8`, with SHA-256
26
+ `046ca168da25d2c0e61c1c15e2b73bfbb67f5208112a4c94fe58fc5aab784f19`.
27
+ Its official commit describes source revision `c4a09096ab401b57f52e092c8f2f829c8fac7628`.
28
+ That metadata is provenance, not a reproducible-build or signed-release guarantee.
29
+ The pin and URLs live in `src/psiphon/config.py`; no moving-master downloads occur.
30
+
31
+ ## Foreground use
32
+
33
+ ```bash
34
+ # No root and no OS route/DNS changes. Leave this terminal running:
35
+ psiphon connect -p -r FR
36
+ # In another terminal, explicitly measure the local proxy:
37
+ psiphon status -p -g --speed -n 3
38
+ curl --proxy socks5h://127.0.0.1:11081 https://api.ipify.org
39
+
40
+ # Native mode, from your normal shell, using the SAME user setup and XDG state:
41
+ psiphon doctor # read-only; does not ask sudo
42
+ psiphon connect -r DE # requests sudo automatically if preflight passes
43
+ # Optional -i eth0 selects the bypass interface.
44
+ ```
45
+
46
+ Setup, doctor, controller, configuration, logs and datastore remain under your
47
+ normal user. There is no root setup or root XDG copy. After read-only preflight,
48
+ connect authenticates with `sudo -v` at the terminal, then launches only the network
49
+ supervisor through noninteractive sudo. Password input is never read from the
50
+ controller/supervisor JSON pipe. If elevation policy replaces that pipe with a TTY,
51
+ the worker refuses before mutations. Cancellation starts no supervisor or route change.
52
+ For agent-operated privilege, launch connect only in an armed tmux pane in the
53
+ agent's current window. No sudo or privileged operation runs in offline checks.
54
+
55
+ **Escalation trust:** sudo approves this local Python interpreter and installed
56
+ package, including an editable checkout if that is what you installed. They may
57
+ be user-writable. `-I` avoids ambient PYTHONPATH/user-site, but does not make that
58
+ code, its environment or its dependencies a tamper-resistant root helper. This is
59
+ trusted local-install escalation, equivalent in trust to running a local script
60
+ through sudo, not a boundary against a compromised account/installation. Prefer a
61
+ reviewed wheel installation; do not add a passwordless sudoers rule for the helper.
62
+
63
+ The elevated worker accepts fixed native operations, not arbitrary command arrays.
64
+ It independently checks engine ownership, path components and the pinned SHA-256,
65
+ then executes a sealed memfd snapshot, preventing executable replacement after the
66
+ hash check. Symlinked engine paths and group/world-writable engine paths/ancestors
67
+ are refused. A local libc memfd API is used if the Python build omits its wrapper.
68
+ The TUN is assigned to the caller UID. The engine runs with the caller UID/GID,
69
+ no supplementary groups, and only its verified descriptor inherited besides
70
+ standard I/O; it reads the
71
+ user configuration and writes the user datastore. Root does not parse the user's
72
+ engine configuration or create root-owned XDG state. Modern Linux permits an
73
+ initial unprivileged SO_BINDTODEVICE; a local socket-only preflight rejects hosts
74
+ where this fails. There is no fallback to a root engine.
75
+
76
+ Region selection is optional: without `-r`, the engine chooses. A requested region
77
+ must match its fresh reported region before readiness succeeds. Ctrl-C stops the
78
+ session and rolls back. `disconnect` requests termination of the owned controller
79
+ in the same user/XDG context; it checks PID start time and exact command identity.
80
+ There is no detached mode. HTTP is `127.0.0.1:18081`, SOCKS is `127.0.0.1:11081`.
81
+ Busy ports are not stolen. Native mode disables both local proxies.
82
+
83
+ ## Native routing and limits
84
+
85
+ ConsoleClient opens a **preexisting named TUN** using `-tunDevice`, binds its
86
+ transport sockets to the uplink with `-tunBindInterface`, and uses
87
+ `-tunDNSServers` for bypass/bootstrap DNS. The wrapper does not use tun2socks.
88
+
89
+ The session supervisor creates only its own TUN, policy table and rules:
90
+
91
+ - IPv4 routes through `psiphon0` in table 18081, priority 101. Priority 100 permits
92
+ explicitly uplink-bound IPv4 sockets to use the unchanged main table. This is
93
+ the engine's escape path, not an application-isolation/security boundary.
94
+ - Local-address routing remains local. Other IPv4 traffic, including LAN traffic,
95
+ goes to the TUN. IPv6 nonlocal traffic is temporarily unreachable; this wrapper
96
+ does not claim tested native IPv6 support. No global IPv6/sysctl switch changes.
97
+ - Reverse-path filtering changes to loose mode only on the uplink and owned TUN;
98
+ previous per-link values are restored. Existing main routes remain untouched.
99
+ - DNS uses upstream's transparent IPv4 resolver **10.0.0.2**, with `resolvectl dns`
100
+ and routing domain `~.` on the owned link. Upstream also defines transparent
101
+ IPv6 DNS `fd19:ca83:e6d5:1c44::2`, unused by this IPv4-only policy.
102
+ - Native preflight requires the systemd-resolved stub, free session resources
103
+ and compatible policy rules. Per-link DNS domains are saved, temporarily cleared
104
+ and restored exactly. Global domains remain configured; their resolver traffic
105
+ also follows the TUN. Private campus/Tailscale names can be unavailable during
106
+ the session. No persistent DNS configuration or resolv.conf is rewritten.
107
+
108
+ The native survey on 2026-10-24 exercised 31 codes on the workstation with
109
+ Tailscale/campus DNS present. Sixteen produced verified packet-tunnel downloads;
110
+ all sessions restored the original routes and per-link DNS domains. See
111
+ `BENCHMARKS.md` for failures and qualifications.
112
+
113
+ Each successful mutation has an inverse. The supervisor owns the engine and
114
+ observes a private pipe: normal exit, startup failure, SIGTERM, Ctrl-C or controller
115
+ SIGKILL closes it, stops/reaps the engine, and rolls back in reverse order. Cleanup
116
+ errors are explicit, never ignored. This is not protection against the supervisor
117
+ itself being killed, host failure, an administrator changing resources mid-session,
118
+ new DNS configuration appearing mid-session, or applications deliberately binding
119
+ to the bypass interface. It is not a permanent kill switch. Ordinary connectivity
120
+ returns after rollback. Do not use this as an anonymity or comprehensive leak-proof
121
+ security boundary. The live validation is scoped to the tested host and fleet.
122
+
123
+ Not every proxy exit supports the packet-tunnel SSH channel. Native readiness
124
+ requires a fresh tunnel notice, matching region, the channel-established diagnostic,
125
+ the active TUN route, and a real HTTPS public-IP response over that route. A proxy
126
+ success is never substituted. Missing packet support times out and rolls back;
127
+ the engine selects/retries its servers. The wait budget bounds readiness polling,
128
+ not OS hostname-resolution stalls. Availability and traffic rules remain fleet-controlled.
129
+
130
+ Official source references, at the pinned source revision:
131
+
132
+ - [ConsoleClient flags and device binding](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c4a09096ab401b57f52e092c8f2f829c8fac7628/ConsoleClient/main.go)
133
+ - [Transparent DNS and packet rewriting](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c4a09096ab401b57f52e092c8f2f829c8fac7628/psiphon/common/tun/tun.go)
134
+ - [Packet SSH channel establishment](https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/c4a09096ab401b57f52e092c8f2f829c8fac7628/psiphon/tunnel.go)
135
+
136
+ ## Status and explicit benchmarks
137
+
138
+ ```bash
139
+ psiphon status # current OS route + HTTPS IP, with or without Psiphon
140
+ psiphon status -g # opt-in third-party country/city lookup
141
+ psiphon status --speed -n 3 # explicit download and upload on the current route
142
+ psiphon status -p -g -d # explicit local proxy instead
143
+ psiphon regions # catalogue + last observed fleet notice
144
+ psiphon bench -r FR -r JP -p # selected proxy regions, no OS route changes
145
+ psiphon bench -a -p # explicit all-region proxy survey
146
+ psiphon bench -a # explicit native survey; sudo supervisor per session
147
+ ```
148
+
149
+ All custom CLI options have short aliases; help is `-h/--help`, version is `-v`.
150
+ Status does not depend on a saved Psiphon PID and never silently chooses the proxy.
151
+ Ambient proxy and NO_PROXY settings are ignored. GeoIP is a database observation,
152
+ not proof of physical location; opting in discloses the measured IP to ipwho.is.
153
+ IP queries and speed tests also contact external services.
154
+
155
+ Downloads stream real payload. Uploads stream at most 40 MB per sample and require
156
+ a successful HTTP response, not just locally queued bytes. Timing includes setup
157
+ and response overhead; `-s` is a measurement/send budget, not an absolute deadline.
158
+ Errors remain errors, never zero-Mbps results. Endpoints live in `config.py`.
159
+
160
+ `bench` requires either `-a` or repeated `-r`. All-region scope is the catalogue
161
+ union observed fleet codes, extended when later notices announce additional codes;
162
+ it is not a claim to discover every server or every future region. Each region is
163
+ attempted once. Private JSONL records errors and measurements, then exact selected
164
+ and attempted sets. It does not run from ordinary connect/status or offline checks.
165
+ Historical proxy measurements remain in [BENCHMARKS.md](BENCHMARKS.md); they do not
166
+ validate the new native path. No standalone benchmark scripts are used.
167
+
168
+ ## State, packaging and development
169
+
170
+ | Data | Default location |
171
+ |---|---|
172
+ | Packaged defaults and verbatim engine license | `src/psiphon/assets/` (also in wheel) |
173
+ | Verified engine and its separate GPL license | `$XDG_DATA_HOME/psiphon/<revision>/` |
174
+ | Active engine configuration | `$XDG_CONFIG_HOME/psiphon/psiphon.config` |
175
+ | Engine server lists/datastore | `$XDG_CACHE_HOME/psiphon/core/` |
176
+ | Explicit benchmark observations | `$XDG_CACHE_HOME/psiphon/bench-<UTC>.jsonl` |
177
+ | Append-only diagnostic log | `$XDG_STATE_HOME/psiphon/psiphon.log` |
178
+ | Controller lock and identity | `$XDG_RUNTIME_DIR/psiphon-<uid>/`, or `/tmp/psiphon-<uid>/` |
179
+
180
+ Standard XDG fallbacks apply. Directories and runtime files are private. Logs and
181
+ benchmarks can contain IPs and diagnostic URLs; do not publish them raw. No account
182
+ or password is stored. Old service assets and management code are removed; upgrading
183
+ does not stop or remove a previously installed OS service automatically. Such a unit
184
+ must be explicitly stopped/disabled before migration; this offline pass does not
185
+ change the installed system or global tool.
186
+
187
+ `make check` synchronizes from the frozen lock **offline**, builds the wheel,
188
+ runs Ruff, compilation, Pyright, offline behavior/failure-injection tests and an
189
+ isolated installed-wheel resource/CLI test. Real pipe EOF/SIGKILL tests use dummy
190
+ processes and temporary files, never a VPN or privileged route command.
191
+
192
+ `make build` builds locally. `make publish` is an explicit remote action and must
193
+ run in an approved `with-env` tmux pane. `make push` uses the current branch and
194
+ both SSH remotes `github` and `gitlab`, failing on errors. Neither is run by checks.
195
+
196
+ ## License and trust
197
+
198
+ Only the original Python wrapper is BUSL 1.1, with the personal/noncommercial
199
+ academic grant in [LICENSE](LICENSE), changing to AGPL-3.0-only no later than
200
+ 2030-10-24. Its legal body is canonical and does not relicense the engine.
201
+
202
+ The external engine remains upstream GPLv3 (source notices specify version 3 or
203
+ later). The exact pinned upstream license is shipped separately at
204
+ `src/psiphon/assets/LICENSE.engine` and copied beside the engine by setup. The wheel
205
+ contains no engine binary. Anyone redistributing the engine must separately meet
206
+ upstream GPL and corresponding-source obligations; the wrapper license grants no
207
+ rights to Psiphon's trademarks or service. Psiphon operates the exit fleet, not
208
+ this wrapper. Neither universal DPI evasion nor anonymity is guaranteed.
@@ -0,0 +1,40 @@
1
+ [project]
2
+ name = "psiphon"
3
+ version = "2.0.0"
4
+ description = "Foreground native Psiphon packet tunnel and explicit local proxy for Linux"
5
+ readme = "README.md"
6
+ license = "BUSL-1.1"
7
+ license-files = ["LICENSE"]
8
+ authors = [
9
+ { name = "KpihX", email = "kapoivha@gmail.com" }
10
+ ]
11
+ requires-python = ">=3.12"
12
+ dependencies = [
13
+ "requests>=2.34.2",
14
+ "rich>=15.0.0",
15
+ "typer>=0.27.3",
16
+ ]
17
+
18
+ [project.scripts]
19
+ psiphon = "psiphon.main:app"
20
+
21
+ [project.urls]
22
+ Repository = "https://github.com/kpihx-labs/psiphon"
23
+ GitLab = "https://gitlab.com/kpihx-labs/vpns/psiphon"
24
+ Issues = "https://github.com/kpihx-labs/psiphon/issues"
25
+
26
+ [build-system]
27
+ requires = ["uv_build>=0.10.3,<0.11.0"]
28
+ build-backend = "uv_build"
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "pytest>=9.1.1",
33
+ ]
34
+
35
+ [tool.pytest.ini_options]
36
+ markers = ["live: explicit opt-in network benchmark, never run by make check"]
37
+
38
+ [tool.psiphon-contract]
39
+ schema_version = "psiphon.contract.v2"
40
+ contract_version = "2.0.0"
@@ -0,0 +1,19 @@
1
+ """psiphon — Linux controller for Psiphon (tunnel-core)."""
2
+
3
+ from importlib.metadata import version
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from psiphon.main import app
8
+
9
+ __all__ = ["app"]
10
+ __version__ = version("psiphon")
11
+
12
+
13
+ def __getattr__(name: str):
14
+ # Keep the worker import independent of the CLI and avoid runpy double imports.
15
+ if name == "app":
16
+ from psiphon.main import app
17
+
18
+ return app
19
+ raise AttributeError(name)