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 +92 -0
- psiphon-2.0.0/PKG-INFO +225 -0
- psiphon-2.0.0/README.md +208 -0
- psiphon-2.0.0/pyproject.toml +40 -0
- psiphon-2.0.0/src/psiphon/__init__.py +19 -0
- psiphon-2.0.0/src/psiphon/assets/LICENSE.engine +675 -0
- psiphon-2.0.0/src/psiphon/assets/psiphon.config +11 -0
- psiphon-2.0.0/src/psiphon/config.py +119 -0
- psiphon-2.0.0/src/psiphon/engine.py +264 -0
- psiphon-2.0.0/src/psiphon/main.py +461 -0
- psiphon-2.0.0/src/psiphon/network.py +118 -0
- psiphon-2.0.0/src/psiphon/routing.py +170 -0
- psiphon-2.0.0/src/psiphon/supervisor.py +295 -0
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.
|
psiphon-2.0.0/README.md
ADDED
|
@@ -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)
|