Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c1a9513
release: Xrayebator 0.5.5
Ap3x0s Sep 24, 2026
2801fb9
fix(ci): ставить libsecret в job test релизного workflow
Ap3x0s Sep 24, 2026
26c27ac
ci(release): preflight-проверка токена и текста релиза, публикация от…
Ap3x0s Sep 25, 2026
21fed1b
docs(testing): описать preflight и выбор автора релиза в release.yml
Ap3x0s Sep 25, 2026
67764d1
ci(release): preflight не валит выкатку, а выбирает автора через output
Ap3x0s Sep 25, 2026
c0d4083
CLI: revoke/expire/bypass-groups — серверный слой для трёх новых GUI-фич
Ap3x0s Sep 28, 2026
e5b8d3c
CLI: bypass groups отдаёт домены, + bypass unbundle (выключение групп)
Ap3x0s Sep 28, 2026
4d09047
GUI: revoke подписки, срок действия профилей, bypass-группы
Ap3x0s Sep 28, 2026
df6cff9
Docs: revoke/срок/bypass больше не «недоступные» возможности GUI
Ap3x0s Sep 28, 2026
091dffe
CLI: revoke/expire/bypass-groups — серверный слой для трёх новых GUI-фич
Ap3x0s Sep 28, 2026
df78876
CLI: bypass groups отдаёт домены, + bypass unbundle (выключение групп)
Ap3x0s Sep 28, 2026
ad0d0fa
GUI: revoke подписки, срок действия профилей, bypass-группы
Ap3x0s Sep 28, 2026
8d74db2
Docs: revoke/срок/bypass больше не «недоступные» возможности GUI
Ap3x0s Sep 28, 2026
2070c62
Docs: синхронизация README/troubleshooting/security/architecture/CHAN…
Ap3x0s Sep 28, 2026
c8101be
Docs: синхронизация README/troubleshooting/security/architecture/CHAN…
Ap3x0s Sep 28, 2026
b2348b8
GUI: предупреждение «скоро истекает» за 3 дня до автоотключения
Ap3x0s Sep 28, 2026
70af718
GUI: предупреждение «скоро истекает» за 3 дня до автоотключения
Ap3x0s Sep 28, 2026
33f4771
Merge main into dev: синхронизация истории перед работой над фичами
Ap3x0s Sep 28, 2026
de805da
Fix: почему фичи «не работали» — старая ветка на сервере + три бага UX
Ap3x0s Sep 28, 2026
aab3296
UI: стабильный календарь, кнопки срока, локальный дефолт updater → dev
Ap3x0s Sep 28, 2026
e01d795
Удаление bypass-подсистемы + 410 Gone для истёкших профилей + календарь
Ap3x0s Sep 28, 2026
7210ebf
Возврат bypass в исходном виде (без наших доработок и без GUI)
Ap3x0s Sep 28, 2026
43363fc
Фикс: ячейка календаря вылезала за рамку (aspect-ratio внутри фикс-вы…
Ap3x0s Sep 28, 2026
e6df138
Возврат дефолта обновления на main (сервер остаётся на dev через .cur…
Ap3x0s Sep 28, 2026
7c98f96
Календарь: квадратные боксы дат, шестая неделя раскрывается вниз
Ap3x0s Sep 28, 2026
8dd1926
Docs: 410 для истёкших сроков и поля профиля (ru/en/zh)
Ap3x0s Sep 28, 2026
e96b78b
Срок действия: время включительное + фикс восьмеричной ловушки bash
Ap3x0s Sep 28, 2026
d84e851
Документация: включительный срок и время сервера (ru/en/zh)
Ap3x0s Sep 28, 2026
17c695e
Timezone срока: сервер отдаёт календарную дату для GUI
Ap3x0s Sep 29, 2026
eb58d85
Документация: описать expire_date и несовпадающие часовые зоны
Ap3x0s Sep 29, 2026
9d649fa
Документация: синхронизация с кодом за 30 коммитов dev
Ap3x0s Sep 30, 2026
05fe976
Документация: подсветка «скоро истекает» и предупреждение о старом се…
Ap3x0s Sep 30, 2026
31378ff
Удаление docs/superpowers из репозитория
Ap3x0s Oct 1, 2026
64c562c
Релизный документ v0.6.0 (ru/en)
Ap3x0s Oct 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

User-facing Xrayebator changes. The server manager and Electron application are published from the canonical `howdeploy/Xrayebator` repository.

## [Unreleased]

### Added

- Subscription revocation in Server settings: a per-profile button opens a menu with two modes. "New link only" reissues the `sub_token`; "full revocation" also rotates the uuid in every inbound of the profile, so devices that already downloaded the configuration are cut off immediately — the only way to actually close access through a leaked link.
- Profile expiry dates: `profile-create --expire`, `profile-expire`, the `expire` field in the profile JSON, a date chip (warning tone within three days before expiry, danger tone after) and an in-theme calendar editor in the GUI (past days disabled, +7/+30/+90/+365 presets; outdated servers without expiry support are reported instead of silently shown as unlimited), plus server-side enforcement — a `xrayebator-expire.timer` systemd unit runs `xrayebator expire-check` every 10 minutes and switches an expired profile off (the client is removed from the inbounds and restored on renewal).
- The subscription-userinfo `expire` header now has a UI path: HAPP and other clients display the date handed out by the subscription.

### Fixed

- Expired or disabled profiles now receive `410 Gone` instead of subscription routes.
- `xrayebator update <branch>` now pins the branch in `.current_branch`: previously the GUI’s "Update Xrayebator" silently rolled an updated server back to the release branch and the new commands disappeared.
- GUI failures now surface the real reason (parsed from stderr, falling back to stdout); an unknown command on an outdated server is answered with an update hint instead of a bare exit code.
- A date-only profile expiry now includes the selected day through `23:59:59` in the server's local timezone; explicit times use that timezone as entered. The server returns its own `expire_date` for GUI display, so a desktop in a different timezone still shows the selected server-local calendar day (covered by a UTC−7 server / UTC+14 client regression test). Date/time fields with leading zeroes such as September (`09`) are accepted correctly.

## [0.5.5] - 2026-09-25

Connecting to servers that already run Xrayebator, plus fixes found while testing on a live VPS.
Expand Down
15 changes: 9 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project

Xrayebator — automated Xray Reality VPN manager for bypassing DPI censorship in Russia. The single Bash application (`xrayebator`, ~11841 lines) turns a VPS into a managed VPN server with an interactive terminal UI. The installer requires Bash, root/sudo, an apt-based Debian/Ubuntu-like system and systemd; the tested matrix is Debian 12/13 and Ubuntu 22.04/24.04. The **active desktop GUI** lives in `src/` (Electron + React + TypeScript) and drives the same Bash CLI over SSH. A legacy PySide6 GUI sits in `gui-legacy/` and is archival, not the active desktop app.
Xrayebator — automated Xray Reality VPN manager for bypassing DPI censorship in Russia. The single Bash application (`xrayebator`, ~12700 lines) turns a VPS into a managed VPN server with an interactive terminal UI. The installer requires Bash, root/sudo, an apt-based Debian/Ubuntu-like system and systemd; the tested matrix is Debian 12/13 and Ubuntu 22.04/24.04. The **active desktop GUI** lives in `src/` (Electron + React + TypeScript) and drives the same Bash CLI over SSH. A legacy PySide6 GUI sits in `gui-legacy/` and is archival, not the active desktop app.

## Validation

There IS automated test coverage (despite what older notes said):
- **`validation/`** — 26 Bash test scripts, including `test-main-readiness-regressions.sh`, covering migrations, VLESS URL generation, transaction safety, dedup, firewall, menu numbering, the bypass/sni-change/port-change CLIs, quickstart, email/inspect regressions, apt-lock race regressions and audit regressions. They run on the host (`bash validation/test-*.sh`); CI installs `jq`, `uuidgen` and `ripgrep` on Ubuntu. A bare Windows Git Bash checkout is not equivalent to the Linux environment.
- **`validation/`** — 28 Bash test scripts, including `test-main-readiness-regressions.sh`, covering migrations, VLESS URL generation, transaction safety, dedup, firewall, menu numbering, the bypass/sni-change/port-change/profile-revoke/profile-expire CLIs, update branch pinning, quickstart, email/inspect regressions, apt-lock race regressions and audit regressions. They run on the host (`bash validation/test-*.sh`); CI installs `jq`, `uuidgen` and `ripgrep` on Ubuntu. A bare Windows Git Bash checkout is not equivalent to the Linux environment.
- **`gui-legacy/tests/`** — 16 pytest modules covering SSH, deploy, connection, subscription and TUN runtime (legacy PySide6 GUI). Run with the GUI venv: `gui-legacy/.venv/Scripts/python -m pytest gui-legacy/tests`.
- **GUI (Electron)** — Vitest unit tests in `tests/`: `npm test`, plus `npm run typecheck`.
- **CI** — `.github/workflows/ci-linux.yml` runs the full `validation/` suite; `.github/workflows/gui-release.yml` runs `ruff` + `pytest gui-legacy/tests` and builds Windows/macOS bundles; `.github/workflows/release.yml` ships the Electron app.
Expand Down Expand Up @@ -133,20 +133,23 @@ Do NOT use raw `jq ... > temp && mv temp file` — always go through `safe_jq_wr

Apart from the interactive menu (`sudo xrayebator`), the script exposes subcommands used by the GUI and by automation. They are dispatched at the very bottom of `xrayebator` (the `case "${1:-}" in ... esac` block guarded by `XRAYEBATOR_SOURCED`):

- `xrayebator update` — update only the Xray-core binary; `xrayebator update <branch>` self-updates the manager from the canonical raw branch and then updates Xray-core.
- `xrayebator update` — update only the Xray-core binary; `xrayebator update <branch>` self-updates the manager from the canonical raw branch, pins that branch in `/usr/local/etc/xray/.current_branch` (the GUI updater reads this file), then updates Xray-core.
- `xrayebator-update [branch]` — separate full `update.sh` lifecycle workflow; without a branch it displays `.current_branch` and opens interactive branch selection.
- `xrayebator quickstart --email <email>` — UI CLI used by the desktop app: runs the broad setup/migration path, provisions the subscription endpoint, creates a standard **schema-v3 multi-route** HAPP profile (7 routes including `xhttp-legacy`), and prints JSON with `subscription_url`. The implementation currently tolerates migration failures in this non-interactive path; verify the resulting profile and services after deployment. Email mode is explicit: `quickstart --without-email` runs the same path but registers Certbot/ACME with `--register-unsafely-without-email` (no renewal notices, no email-based account recovery; never substitute a fake address). The GUI passes exactly one of the two forms.
- `xrayebator inspect --json` — read-only install probe for the GUI "connect existing server" import: reports manager/Xray/profiles/subscription markers as one JSON object on stdout (diagnostics to stderr only). It must not migrate, install, restart, open firewall or write anything; `validation/test-quickstart-email-and-inspect.sh` guards these invariants statically.
- `xrayebator happ-setup` — reduced existing-install HAPP path; ensures the subscription service and a usable multi-route profile, verifies a real public TLS endpoint when subscription markers are missing, and prints JSON with `subscription_url`. It does not have the same migration breadth as quickstart.
- `xrayebator probe-test` — probe-test candidate SNIs from `sni_list.txt` and print reachability scores.
- `xrayebator profiles` — print all profiles as a flat JSON array (used by the GUI "Server settings" page).
- `xrayebator profile-create --name NAME [--transport T] [--port P] [--count N]` — create 1..N profiles non-interactively (names `name`, `name-2`, ...). Emits `{"ok":true,"names":[...],"errors":[...]}`; `ok` stays `true` even when some profiles already exist (they land in `errors`).
- `xrayebator profiles` — print all profiles as a flat JSON array (used by the GUI "Server settings" page); each entry carries `expire` (epoch seconds, `0` = unlimited), server-local `expire_date` (`YYYY-MM-DD`) for display, and `expire_disabled`. `expire_supported` is present (`true`) only on servers with expiry enforcement; its absence means the server predates the feature and the GUI must not offer expiry.
- `xrayebator profile-create --name NAME [--transport T] [--port P] [--count N] [--expire DATE]` — create 1..N profiles non-interactively (names `name`, `name-2`, ...). `--expire` accepts `YYYY-MM-DD[ HH:MM]`, epoch seconds or 13-digit milliseconds; date-only expiry is inclusive through `23:59:59` in server-local time, while an explicit time uses server-local time as given. Dates/times with leading-zero fields such as `09` are parsed as decimal, not octal. A past expiry is rejected. Emits `{"ok":true,"names":[...],"errors":[...]}`; `ok` stays `true` even when some profiles already exist (they land in `errors`).
- `xrayebator profile-delete --name NAME` — delete a profile, emits `{"ok":true,"name":"..."}`. Inbound/firewall cleanup happens automatically.
- `xrayebator profile-revoke --name NAME [--full]` — reissue the subscription `sub_token`; `--full` also rotates the uuid in every inbound of the profile, so already-downloaded configs stop connecting. Transaction order matters: config + `safe_restart_xray` first, then the profile JSON, and a failed profile write rolls the config back from the transaction backup.
- `xrayebator profile-expire --name NAME --expire DATE|epoch|none` — set/extend/clear a profile expiry (`.expire` epoch seconds); date-only expiry is inclusive through `23:59:59` in server-local time; an explicit time is also interpreted in server-local time. Applied immediately (an expired profile loses its client, an extension restores it).
- `xrayebator expire-check` — batch enforcement of every due expiry; idempotent (no changes ⇒ no Xray restart), driven by `xrayebator-expire.timer` every 10 minutes. Disabling keeps the inbounds alive (a fresh inbound would change the shortId and kill issued URLs) and snapshots clients into `.expire_clients` for restoration.
- `xrayebator fp-change --name NAME [--route R] --fp FINGERPRINT` — change the fingerprint for a profile (client-side, no Xray restart), emits JSON.
- `xrayebator sni-change --name NAME [--route R] --sni SNI` — change the SNI for a profile; updates all profiles on the same port (`update_all_profiles_on_port()`), emits JSON.
- `xrayebator sni-list` — print the SNI candidates from `sni_list.txt` grouped by category, emits JSON (used by the GUI SNI dialog).
- `xrayebator port-change --name NAME [--route R] --port PORT|random` — change the port for a profile; updates the inbound, firewall, subscription and all profiles on the port, emits JSON (reconnect is required).
- `xrayebator bypass list|add --domain D|remove --domain D|reset|bundle [--group a,b,c]` — manage bypass routing groups (JSON).
- `xrayebator bypass list|add --domain D|remove --domain D|reset|bundle [--group a,b,c]` — manage bypass routing groups (JSON). Server-side only: it exists for cascade deployments, where the catch-all points at the upstream; without a cascade the built-in catch-all already sends everything direct.

CLI JSON hygiene: `profile-create`/`profile-delete` **must** print only JSON on stdout. The shared helpers (`backup_config`, `add_inbound`, `open_firewall_port`, `safe_restart_xray`, `close_firewall_port`) print colored status lines that would corrupt the parse, so the CLI paths redirect stdout→stderr around those calls (`exec 3>&1; exec 1>&2 ... exec 1>&3`). Keep it that way when editing.

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ about.
| HAPP subscription | Serves the `vless://` list, a managed Global Proxy routing profile and token-protected geo databases; nginx publishes it over HTTPS | `subhttp.sh`, `xrayebator-sub.service` |
| Post-quantum XHTTP | The `xhttp-pq` route runs VLESS encryption `mlkem768x25519plus` | `.vless_encryption`, `.vless_decryption` |
| v2ray compatibility | `v2rayNG` and `v2rayN` receive a classic base64 body without HAPP metadata | `subhttp.sh` |
| Subscription revoke | Generates a new 32-character hex token; the old URL stops working | `openssl rand -hex 16` |
| Subscription revoke | Generates a new 32-character hex token; the old URL stops working. Full revocation (`--full`) also rotates the uuid, so already-downloaded configs stop connecting | `openssl rand -hex 16`, `profile-revoke` |
| Profile expiry | A profile can carry an expiry date; `xrayebator-expire.timer` switches the client off the inbounds when it passes and restores the same uuid on renewal | `profile-expire`, `expire-check` |
| Bypass routing | Seven domain groups can be sent straight through `freedom`, skipping the VPN | menu `7` |
| Cascade | Switches the `tcp,udp` catch-all to a foreign VLESS Reality upstream of type `tcp` or `xhttp` | `upstreams/cascade.json` |
| Self-steal stub | Puts nginx with a valid certificate on `127.0.0.1:9444` and points a Reality fallback at it | menu `9` |
Expand Down Expand Up @@ -153,7 +154,6 @@ vless:// list — HAPP receives 6 of the profile's 7 routes
▼
Reality inbound on a port in 30000-60000 (User=xray, CAP_NET_BIND_SERVICE)
│
├─ domain in an enabled bypass group ──► freedom (direct, no VPN)
│
└─ all other tcp/udp ─────────────────► direct
OR cascade-upstream ──► foreign VPS
Expand Down Expand Up @@ -294,12 +294,13 @@ What the GUI can do:
| Add server | Deploy a new VPS with an explicit email choice: `quickstart --email` or `quickstart --without-email`; save the server and public subscription |
| Connect existing | Import a recognized Xrayebator installation over SSH (password or key) using read-only `xrayebator inspect --json`; partial installs are saved with diagnostics, without automatic repair |
| Server keys | Refresh the public subscription, copy the URL, show `vless://` links and QR codes |
| Server settings | SSH access by password or private key, direct root or sudo; list/create/delete profiles, change fingerprint, SNI and port, plus update or uninstall Xrayebator on the server |
| Server settings | SSH access by password or private key, direct root or sudo; list/create/delete profiles, change fingerprint, SNI and port, revoke the subscription, set a profile expiry date, plus update or uninstall Xrayebator on the server. Expiry dates are displayed in the server's timezone even when the desktop uses another one |

Root + password is the one-click default; key authentication and sudo are optional. A selected private key and a successfully used SSH login password are stored in the operating-system keychain via `keytar` and reused across later SSH operations and app restarts; the server card keeps only their non-secret credential ids and display name. A distinct sudo password and an encrypted-key passphrase are never persisted and are requested again when needed. If the OS keychain is unavailable, there is no plaintext fallback: the secret remains in main-process memory for the current app session and the UI warns that it must be entered again after restart. The app also persists the `subscription_url`, fetched `vless://` links and pinned SSH host-key fingerprint. The subscription URL and VLESS links are bearer/client credentials: protect local app data and revoke the subscription through the terminal workflow after a leak.
Root + password is the one-click default; key authentication and sudo are optional. A selected private key and a successfully used SSH login password are stored in the operating-system keychain via `keytar` and reused across later SSH operations and app restarts; the server card keeps only their non-secret credential ids and display name. A distinct sudo password and an encrypted-key passphrase are never persisted and are requested again when needed. If the OS keychain is unavailable, there is no plaintext fallback: the secret remains in main-process memory for the current app session and the UI warns that it must be entered again after restart. The app also persists the `subscription_url`, fetched `vless://` links and pinned SSH host-key fingerprint. The subscription URL and VLESS links are bearer/client credentials: protect local app data and revoke the subscription from Server settings (full revocation rotates the key as well) after a leak.

The GUI exposes only a subset of the terminal menu. Bypass, `probe-test`, subscription revoke,
`happ-setup`, cascade, self-steal and service logs/status remain terminal-only. See
The GUI exposes only a subset of the terminal menu. Bypass, `probe-test`, `happ-setup`, cascade,
self-steal and service logs/status remain terminal-only; profile expiry dates and subscription
revocation are available in Server settings. See
[Electron Desktop GUI](docs/desktop-gui.md) for the complete boundary, security model and packaging details.

Build and run in the development mode:
Expand Down
Loading
Loading