waypoint
Turn client-side reachability measurements into a reviewable plan for which users move to which front when one gets blocked. Pure Python, zero dependencies.
It is a decision tool, not a proxy and not a panel. It runs no tunnel, serves no traffic, holds no obfuscation core, and generates no load. It reads an aggregated reachability summary and emits a plan — a list of recommended rotations with reasons and confidence — that the operator reads and applies deliberately. It is the "hands" to sniprobe's "eyes."
$ waypoint plan --endpoints fleet.json --reachability agg.json
Recommended rotations (1):
AS 12389: move A -> S [confirmed] front a.example.com confirmed interfered on AS 12389 (interfered>=3, clear=0); moving cohort to clear standby s.example.com
Warnings (1):
- no clear standby for AS 8359; users of B left in place
Decide, don't act
The engine emits a plan; applying it is a separate, explicit step behind an actuator boundary. The only actuator shipped here is a dry run — it renders what would happen and touches nothing.
The reason is a safety property. A bug in the decision logic must produce a wrong recommendation the operator can inspect and reject, never an automatic reconfiguration that breaks everyone's access at once. There is no flag in this version that touches real infrastructure; --apply runs the dry-run actuator and says so loudly.
What it does not rebuild
The circumvention ecosystem already solves, well and audited, everything but one thing. waypoint rebuilds none of it:
- The tunnel and obfuscation core — Xray / REALITY do that.
- One-click server install — existing installers (Amnezia and others) do that.
- SNI selection from a server vantage — Hiddify-Reality-Scanner, RealityChecker and others do that.
- Per-user config / subscription / QR / revocation — panels like 3x-ui and Marzban do that.
The gap none of them fill is the one this fills: consume client-side reachability and decide, per operator, when to rotate a front or activate a warm standby, and for whom. Until there was an input to decide on, the gap could not be filled.
Input: an aggregate, never raw reports
The engine consumes the aggregated, k-anonymity-thresholded reachability summary that sniprobe aggregate --json produces — keyed by candidate-SNI × AS, never raw per-volunteer reports. It never handles a volunteer's identity or address, and never handles a user list either: it decides at endpoint × operator-AS granularity ("for users of endpoint E on AS 12389, move to standby S"), and which real users that is stays local to the operator.
waypoint does not import sniprobe; it consumes the documented JSON shape:
{
"networks": [8359, 12389],
"candidates": {
"front.example.com": {
"overall": "interfered",
"totals": {"clear": 0, "interfered": 2, "unreachable": 0, "inconclusive": 0},
"by_network": {
"8359": {"clear": 3, "interfered": 0, "unreachable": 0, "inconclusive": 0},
"12389": {"clear": 0, "interfered": 3, "unreachable": 0, "inconclusive": 0}
},
"suppressed_networks": []
}
}
}
A cell absent from by_network — suppressed by k-anonymity, or simply never observed — means no usable data for that (sni, AS). The engine treats it as inconclusive and never acts on it.
The endpoint inventory is a JSON list (or {"endpoints": [...]}):
[
{"id": "A", "sni": "a.example.com", "role": "active"},
{"id": "S", "sni": "s.example.com", "role": "standby", "note": "warm"}
]
The rules the engine enforces
- Hysteresis / no-flap. It recommends a switch only on confirmed, sustained interference — at least
--min-reportsinterfered observations with no clear observation in that cell. A mixed cell (any clear) is ambiguous and never actionable. Given an optional list of recent changes it will not re-switch a front it just moved. - Smallest blast radius. An action targets exactly one
(endpoint, AS)cohort. A front blocked on one operator and clear on three triggers a move only for the blocked one — never "rotate everyone because one operator changed." Only real, numeric ASes are acted on;sniprobe's"unknown"catch-all bucket (unresolved operators) is never actionable and surfaces as a warning. - Fail-safe. It moves a cohort only onto a standby that is confirmed clear on that AS — meaning
interfered == 0,clear >= --min-reports, andclearis the dominant non-interfered outcome (clear >= unreachable + inconclusive). A standby that clears the threshold but is mostly unreachable is a worse front, not a safe one, and is refused. If no confirmed-clear standby exists it emits a warning, not an action — churning users from one blocked front onto a blocked or unreachable one is worse than leaving them. - Diversity floor. It refuses to collapse the fleet onto a single SNI: a move that would drop the number of distinct in-service SNIs on an AS below
--diversity-flooris held back, and when several fronts are blocked at once it spreads their cohorts across distinct standbys rather than funnelling them onto one. - No load, ever. Its only outputs are rotation recommendations for the operator's own fronts. There is no code path that recommends generating traffic or acting against the filter.
Output is deterministic — actions are sorted by AS then endpoint id — so a reviewer, and a test, can pin the plan exactly.
The actuator is a boundary; the panel adapter is yours
A real adapter — one that talks to 3x-ui, Marzban, or another panel to activate a standby or move a cohort — is a separate, operator-supplied integration and is deliberately not shipped here. It is the operator's own infrastructure and choice, and those panel API surfaces must not be guessed into the core. Implement the Actuator contract (src/waypoint/actuator.py) against your own panel if and when you want to apply plans; the engine's correctness does not depend on any panel.
Install
Zero runtime dependencies; Python 3.8+.
pip install waypoint-sni # once published
waypoint plan --endpoints fleet.json --reachability agg.json
--json emits the machine-readable plan. --min-reports and --diversity-floor tune the two thresholds above.
Development
git clone https://github.com/Canavalny/waypoint
cd waypoint
python3 -m pytest -q # 44 tests, no dependencies beyond pytest
The engine is a pure function, so every rule above is tested offline against constructed reachability summaries — no network, no clock, no panel.
Applying a plan: the Marzban adapter
The engine only decides. waypoint.marzban.MarzbanActuator is the first real
adapter that applies — it talks to a Marzban
panel to make a plan's standby fronts available. It authenticates, reads the
panel's hosts, and adds a host for each standby SNI a plan wants to move users
onto (GET/PUT /api/hosts). It never removes a host, and it defaults to a
previewable, operator-in-the-loop flow.
One honest limit is built in: Marzban serves every user one subscription, so
there is no per-AS-cohort steering to automate — and waypoint never holds the
volunteer→AS mapping that would be needed anyway (privacy). The adapter does the
part it can (ensure the standby is available) and emits a precise operator
worklist for the part it cannot (move the specific cohort). It is a library
integration you wire to your own panel and credentials; the CLI stays dry-run.
Verified how far: the adapter's request-building and response-parsing are unit-tested against a fake transport (auth, ensure-if-missing, idempotence, never-remove, worklist, warnings). It has not been run against a live Marzban panel, so treat live-API compatibility as unverified until you point it at your own instance.
Optional before publishing to PyPI: decide whether to fill in
authorsinpyproject.toml(currently commented out) and theLICENSEcopyright line (currently "waypoint contributors"). The repository URLs are already set.
License
MIT.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file waypoint_sni-0.1.0.tar.gz.
File metadata
- Download URL: waypoint_sni-0.1.0.tar.gz
- Upload date:
- Size: 26.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d0c7ea4441593b386cb85211383805278ac12fded5490997fcc903daf0b87a5
|
|
| MD5 |
f1655751badf55bc6634ea7b0d522c42
|
|
| BLAKE2b-256 |
0854363a94eb973d77cbce627c46ccd2b2d5f73b35eac3a0bfeb96f62f44e6f6
|
Provenance
The following attestation bundles were made for waypoint_sni-0.1.0.tar.gz:
Publisher:
release.yml on Canavalny/waypoint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
waypoint_sni-0.1.0.tar.gz -
Subject digest:
7d0c7ea4441593b386cb85211383805278ac12fded5490997fcc903daf0b87a5 - Sigstore transparency entry: 2367605733
- Sigstore integration time:
-
Permalink:
Canavalny/waypoint@2515604abbc41924e61cdc0fa18895be50f99a7f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Canavalny
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2515604abbc41924e61cdc0fa18895be50f99a7f -
Trigger Event:
push
-
Statement type:
File details
Details for the file waypoint_sni-0.1.0-py3-none-any.whl.
File metadata
- Download URL: waypoint_sni-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8365a3dff6b9c6d6217f9d48f477c32fe31b91f904af85d8e4d47f606ac35e4c
|
|
| MD5 |
09909aa8200bedeae9cf9b09419b108e
|
|
| BLAKE2b-256 |
4cf94e198d15d72035b20bde742034437c8838bd3ee6b930880c3b40f18013fe
|
Provenance
The following attestation bundles were made for waypoint_sni-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Canavalny/waypoint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
waypoint_sni-0.1.0-py3-none-any.whl -
Subject digest:
8365a3dff6b9c6d6217f9d48f477c32fe31b91f904af85d8e4d47f606ac35e4c - Sigstore transparency entry: 2367606322
- Sigstore integration time:
-
Permalink:
Canavalny/waypoint@2515604abbc41924e61cdc0fa18895be50f99a7f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Canavalny
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2515604abbc41924e61cdc0fa18895be50f99a7f -
Trigger Event:
push
-
Statement type: