Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d4817ba
ci: add workflow to cross-compile libflclash.so for OHOS
Jul 22, 2026
79d0227
ci: clone core/gvisor at branch tip (pinned commit gone upstream)
Jul 22, 2026
1bc44ee
ci: probe GOOS=ohos target instead of -tlsmodegd flag
Jul 22, 2026
4d44d97
ci: use go1.23 fork with GOOS=linux + c-shared + -tlsmodegd
Jul 22, 2026
72c8ddf
ci: try ohos_golang_go master branch (go1.23/1.24 lack -tlsmodegd)
Jul 22, 2026
6e3987b
ci: build -tlsmodegd toolchain by patching stock go1.24
Jul 22, 2026
55a46e1
ci: patch stock golang/go, not the OHOS fork
Jul 22, 2026
92e0278
ci: 3-way apply the patch to handle go1.24.x point drift
Jul 22, 2026
c5a4399
ci: use moodyhunter/go-tls-mode-gd fork directly (drop patch dance)
Jul 22, 2026
fa84727
ci: clone core/gvisor from their ohos branch (has go.mod)
Jul 22, 2026
7cd3148
ci: build with -mod=mod to resolve stale go.sum against ohos core
Jul 22, 2026
2894e69
ci: regenerate flclash go.mod so ohos core drives dep versions
Jul 22, 2026
26a4d78
ci: shim renamed core APIs (Current/Total, setNetInterfaces)
Jul 23, 2026
081ffe1
ci: harden reproducible OHOS core build
Jul 23, 2026
b4bea00
ci: build and sign installable HarmonyOS HAP
Jul 23, 2026
2fe90b6
ci: trust mounted workspace in HAP container
Jul 23, 2026
8bc0695
ci: vendor pinned UI components for cloud builds
Jul 23, 2026
b942dc5
ci: avoid unavailable file utility in HAP image
Jul 23, 2026
b4c7371
ci: expose bundled Node.js to HAP build steps
Jul 23, 2026
5ef7fe4
ci: build HAP with current HarmonyOS 6.0.2 tools
Jul 23, 2026
dc11f8b
ci: match unsigned HAP output precisely
Jul 23, 2026
703aeaa
ci: keep core checksum outside packaged HAP
Jul 23, 2026
956ffb1
docs+build: LTS stable replacement build for long-lived connections
jerry-271828 Aug 11, 2026
f67d506
docs: synchronized-FIN investigation reports and LTS validation results
jerry-271828 Aug 12, 2026
3ba7b75
feat: add HarmonyOS desktop tray support
jerry-271828 Aug 13, 2026
3878bdc
fix: stabilize HarmonyOS status bar lifecycle
jerry-271828 Aug 13, 2026
2cc67cb
fix: refresh proxy duration after tray restore
jerry-271828 Aug 13, 2026
c03b414
fix: synchronize tray proxy selection state
jerry-271828 Aug 13, 2026
2d75873
fix: hold continuous task while proxy runs to survive screen lock
jerry-271828 Aug 14, 2026
d104ca4
chore: sync generated build profile files (debug mode)
jerry-271828 Aug 14, 2026
e74a8fb
ci: build and publish unsigned release HAP on version tags
jerry-271828 Aug 14, 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
409 changes: 409 additions & 0 deletions .github/workflows/build-core.yml

Large diffs are not rendered by default.

504 changes: 504 additions & 0 deletions .github/workflows/release-hap.yml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
proxy_core/src/flclash/libflclash.so
/key
/.*
!/.github
run.sh
internalTesting
profiler
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@
[submodule "proxy_core/src/flclash/gvisor-ohos"]
path = proxy_core/src/flclash/gvisor-ohos
url = https://github.com/likuai2010/gvisor-ohos.git
[submodule "xb_components"]
path = xb_components
url = https://gitee.com/xiaobai-studio/xb_components.git
4 changes: 2 additions & 2 deletions AppScope/app.json5
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"app": {
"bundleName": "org.xbgroup.clashboxLTS",
"vendor": "example",
"versionCode": 1007047,
"versionName": "1.7.4",
"versionCode": 1007048,
"versionName": "1.7.4-lts-stable.1",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
Expand Down
362 changes: 362 additions & 0 deletions INVESTIGATION-2026-08-11-synchronized-fin.md

Large diffs are not rendered by default.

92 changes: 92 additions & 0 deletions LTS-BUILD-VALIDATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# ClashBox LTS stable build — install & validation guide

## The build

- Branch: `fix/lts-stable-long-connections` @ `7eff34ad` (jerry-271828/ClashBox)
- Base: public LTS `master` @ `1fdc47eb` + reproducible CI; **zero app-logic
changes** (audit: `docs/lts-connection-lifecycle-audit.md` — no automatic
ClearConnections caller exists in LTS; nothing to patch)
- Artifact: `ClashBox-7eff34adbd99-openharmony-test-signed.hap`
sha256 `57fba87e4d4f54cbfafd8b3557585cada4bce4102ade96968a36d83b0b5695c0`
local copy: `/storage/Users/currentUser/tmp/hap-lts-stable/ClashBox-openharmony-test-signed-hap/`
(re-fetch: `gh run download 31545071671 -R jerry-271828/ClashBox`)
- Identity: bundle `org.xbgroup.clashboxLTS`, versionName `1.7.4-lts-stable.1`,
versionCode 1007048 — coexists with store V2 (`org.xbgroup.clashbox`).

## 1. Install

Open the .hap in Files. If retail HarmonyOS rejects the OpenHarmony test key:
put Huawei developer material into the six repo secrets (`HAP_SIGNING_P12_B64`,
`HAP_SIGNING_CERT_B64`, `HAP_SIGNING_PROFILE_B64`, `HAP_KEY_ALIAS`,
`HAP_KEY_PASSWORD`, `HAP_STORE_PASSWORD`) and re-run the workflow
(`workflow_dispatch` supported) — it then produces a device-valid signed HAP.
Do not commit secrets.

## 2. Prepare

1. Stop the store V2 ClashBox (only one VPN at a time).
2. In the LTS app: import the same profile (e.g. the local `sub.txt` file or
subscription URL), select a node, start the VPN.
3. Confirm `127.0.0.1:7890` (mixed) and `127.0.0.1:9090` (controller) respond.

## 3. Long-lived-connection validation (automated)

From a terminal:

sh /storage/Users/currentUser/tmp/mihomo-standalone-k3X9q/run-lts-validation.sh

(Default 3600 s; override with `DURATION=...`. Uses `hmos_wss_probe`, 4 legs:
TUN ×2 + mixed-port ×2, all to `wss://ws.postman-echo.com/raw`, 3 s pings,
controller monitor at 20 Hz.)

Success criteria (old V2 bug would fail within ~3 minutes):

- zero `websocket_error` events in all four legs for the full run
- controller tracker IDs stable; no `connections: null` transitions
- works identically while you foreground / minimize / restore the LTS window
during the run (the script spans long enough to do both)

## 4. Manual clear regression (UI)

During a validation run: open 连接管理/Connect page → tap the clear
(connections) action. Expected: all four legs die together with FIN (that's
the intended manual behavior), controller table empties, then the LTS app
keeps working and new connections succeed.

## 5. Codex real-world validation

With LTS as the active VPN, run normal Codex sessions that stream >3 minutes
(e.g. a long refactor). Success = no
`peer closed connection without sending TLS close_notify` from the periodic
wipe pattern. Keep `CODEX_WS_TRANSPORT_DIAGNOSTICS=1` if you want the
transport-level proof in codex logs.

## What to expect at a glance

| Action | Active flows |
| --- | --- |
| nothing / UI foreground / minimize / restore | survive |
| switch node, switch rule mode, open pages | survive |
| switch/load profile (favorite tap, config-page load) | **terminated** (deliberate: config actually changes) |
| manual "clear connections" | **terminated** (deliberate) |
| core recovery after genuine core death | restarted anyway (deliberate) |

## Validation results (2026-08-12, device run)

Run 1 (`lts-validation-20260811T234621Z`, 23:46–00:03 UTC): 4 legs (2×TUN
fake-ip + 2×mixed 7890), 17.4 min, **zero automatic events**. At 00:03:45 the
user tapped the manual clear: all 4 legs received synchronized FIN within
4 ms — manual ClearConnections works as designed.

Run 2 (`lts-validation-20260812T000702Z`, 00:07–01:14 UTC): 4 auto-restarting
legs, 67 min wall, ~4400 pings/pongs total:

- **no ~180.5 s periodic wipe** (the V2 bug would have fired ~22 times);
- **no global ClearConnections event** (no 4-leg synchronized FIN);
- proxy legs: 0 errors end-to-end;
- one isolated event at 00:32:04.8: both TUN legs reset with TCP RST 10 ms
apart after ~580–600 s life, mixed-port legs unaffected. Classified as a
TUN/gVisor-layer abortive reset (one-off, not FIN, not global, not
periodic) — distinct from the V2 bug signature; noted for tracking, one
occurrence per ~67 probe-hours... (per-run rate: 1 per 4 leg-hours).
- UI foreground/minimize/restore cycles during the runs: no effect on flows.
59 changes: 59 additions & 0 deletions UPSTREAM-ISSUE-DRAFT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# 上游 Issue 草稿(提交至 xiaobaigroup/ClashBox,可关联 #158)

## 标题

[问题报告 BUG] 商店版(V2/2.0.x)每隔约 180.5 秒周期性清空全部活动连接(长连接被同步 FIN)

## 正文

### 现象

ClashBox 商店版(org.xbgroup.clashbox,V2/2.0.x)运行时,所有活动连接(TUN/Fake-IP、
localhost mixed/socks 入站、无关的不同远端、不同建立时长的连接)会在同一时刻被
关闭。应用侧表现为有序 FIN:read()==0、POLLRDHUP、SO_ERROR==0、TCP_INFO=CLOSE_WAIT;
TLS 上层(rustls)报 "peer closed connection without sending TLS close_notify"。
Mihomo 控制器在同一时刻 connections 变为 null(全部 tracker 被移除),随后立刻出现
全新 ID 的连接。ClashBox 两个进程、vpn-tun 网卡、路由表、系统网络回调全部正常。

### 周期

对 8 个 wipe 时刻做最小二乘拟合:周期 180.5004 s,52 个周期内残差 < ±0.5 s。
这是软件定时器,不是网络抖动。网格相位锚定在 ClashBox(VPN/核心)启动时刻
+约 3 秒,而不是绝对墙钟;重启 ClashBox 后网格相位随新实例平移。

### 关键判据(可复核)

1. 对照实验(同机同网):ClashBox 关闭时,独立运行的 mihomo v1.19.27(相同订阅
配置)+ 探针存活 11 个完整网格周期无任何 wipe;ClashBox 开启后,每个可观测
网格周期都会清掉全部连接(包括走 ClashBox VPN 的独立 mihomo 的连接)。
2. 每个 wipe 时刻,空闲的 HTTP/1.1 keep-alive 控制器连接(127.0.0.1:9090)存活。
若是非 patch 的 Load(method 12):hub.ApplyConfig → route.ReCreateServer →
httpServer.Close(),会关闭该连接(metacubex/http v0.1.6 server.go:3082)。
因此周期性 RPC 不是非 patch Load。
3. wipe 时刻核心日志中**没有** "RESTful API listening at"(每次 ReCreateServer
都会打这行 info 日志,hub/route/server.go:174)。再次排除 Load。
4. 综合 2、3:周期性 RPC 是 method 11(ClearConnections)。公开 LTS 源码中它的
唯一调用方是"连接管理"页面的手动清理按钮(Connect.ets:147-157)。因此周期性
调用方只存在于未公开的 V2 代码中。
5. 该周期任务只在 ClashBox **UI 进程正在执行**(前台,或持有后台保活:长时任务/
模拟画中画等)时运行;UI 进程被冻结(CPU 计数完全不动)时,网格 wipe 停止,
但 VPN 转发正常。

### 影响

- 任何超过约 180 s 的长连接(WebSocket、SSE、HTTP/2 长连接、下载、SSH 等)
必然在下一个网格时刻被掐断;对 TLS 上层表现为 UnexpectedEof。
- 正常用户(开启后台保活)全天候受影响;表现为"代理周期性地全部断线又瞬间恢复"。

### 请求维护者确认

V2 中是否存在一个约 180 s 周期的 ArkTS 定时任务,会经 clash_go.sock 调用
ClearConnections(method 11)?(候选方向:连接管理/状态同步/健康检查/自动更新
相关逻辑。)该定时任务不应在例行执行中清空全部活动连接;手动清理功能本身需要保留。

### 环境

- HarmonyOS PC(鸿蒙电脑),HongMeng Kernel 1.12.0;商店版 ClashBox(V2 线,
控制器报告 Mihomo 1.19.27)。
- 注:公开仓库当前为 LTS 线(1.7.4/master 1fdc47eb),与商店版无源码对应关系;
以上 2、3 两条判据依赖商店版内核在这两个路径上与公开 v1.19.27 行为一致。
81 changes: 81 additions & 0 deletions VERIFICATION-PROTOCOL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# ClashBox periodic-wipe — remaining verification protocol (manual steps)

Automated evidence collection has reached the limit of what the unprivileged
shell can observe. The steps below require GUI access to the device.

## A. What is already established (no further action needed)

1. With ClashBox OFF: standalone mihomo + probes survive 11+ full grid cycles.
2. With ClashBox ON: wipes occur on a ~180.5004 s grid, phase-anchored ~3 s
after the VPN/core process start.
3. The periodic RPC is ClearConnections (method 11) with high confidence:
- an idle controller keep-alive connection survived an epoch (a non-patch
Load would have closed it via httpServer.Close);
- no "RESTful API listening at" log line at the epoch (a non-patch Load
would have emitted one via ReCreateServer).
4. The periodic caller does not exist in public LTS source → V2-specific.
5. Under the newest instance the wipe fired at 12:50:52 (≈6 cycles after the
12:32:46 VPN start) and then stopped firing at 12:53:53/12:56:53 — the
caller's activation is conditional (see D).

## B. Optional but decisive: install the instrumented LTS build

Purpose: (a) prove public LTS does not wipe (V2-specificity by experiment, not
just by source audit); (b) if it does wipe, the [NETDIAG] log names the method.

1. Get the HAP: GitHub Actions run 31484200385 (jerry-271828/ClashBox, branch
diag/close-trigger-instrumented), artifact `ClashBox-openharmony-test-signed-hap`.
(Local copy may be incomplete — downloads die at wipe epochs; use a
resumable downloader, or download while VPN is off.)
2. Install: open the .hap in Files. It is signed with the OpenHarmony test key.
If installation is refused, add Huawei developer signing material as the six
`HAP_SIGNING_*` repo secrets and re-run the workflow (docs/ci-hap-signing.md).
3. Import any working profile, start the VPN (installed V2 must be stopped
first — one VPN at a time).
4. Watch the core log (the wrapper exposes the mihomo log stream; or capture
hilog) for lines containing `[NETDIAG]`:
- if NO `close_all_connections_begin` appears across ≥5 predicted epochs →
LTS does not wipe → periodic caller is V2-only (expected);
- if it appears, the preceding `ipc_request_received method=N` names the
method and the reason string names the path.

## C. V2 settings bisection (A/B/A)

The wipe grid restarts its phase when ClashBox restarts. Use that: after each
settings change, fully stop and start ClashBox, note the VPN process start
moment, and predict epochs as start+~3 s + n·180.5004 s.

For EACH candidate below: disable it → restart ClashBox → keep ≥2 long-lived
WSS probes (TUN + 127.0.0.1:7890) running → watch ≥5 predicted epochs
(~15 min). If wipes stop, re-enable and confirm they return (A/B/A).

Candidate order (most plausible first, given the README notes that 核心恢复 is
auto-enabled by 后台运行-模拟画中画):

1. 后台运行 / 模拟画中画 (Background run / simulated PiP) and any 核心恢复
(core recovery) option.
2. 订阅/配置自动更新 (profile auto-update) — set to off, or a long interval.
3. 通知/实况窗 (permanent notification / LiveView) toggles.
4. 长时后台任务/模拟下载/模拟定位 (other background-keepalive modes).
5. Any 连接管理/自动清理/网络优化-style toggle present only in V2.

The epoch recorder (`run-epoch-watch.sh`) automates the detection: it keeps
auto-restarting probes through both paths and logs every wipe with ms
timestamps; just leave it running during the bisection.

## D. Live hypothesis under test (as of 12:58 UTC)

The caller fires every ~180.5 s only while some condition holds (it held
11:40–11:52 and at 12:50:52, then stopped). The lifecycle hilog capture
(`epoch-watch-*/clashbox-lifecycle.hilog`) timestamps UI activity of the
ClashBox process; correlate the next active/inactive transition with what the
user was doing (app foreground/background, PiP shown/closed, screen on/off).

## E. Upstream report

File to xiaobaigroup/ClashBox referencing issue #158; include:
- the grid fit (180.5004 s, ±0.5 s over 52 cycles);
- the OFF/ON A/B (11 clean cycles vs every-epoch wipes);
- the two method-11 discriminators;
- the phase-anchoring to instance start;
- this protocol's bisection outcome once known.
14 changes: 5 additions & 9 deletions build-profile.json5
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
}
}
],
// 产品配置
"products": [
{
"name": "default",
Expand All @@ -43,8 +42,7 @@
},
"externalNativeOptions": {
"abiFilters": [
"arm64-v8a",
"x86_64"
"arm64-v8a"
]
}
}
Expand All @@ -62,16 +60,15 @@
},
"externalNativeOptions": {
"abiFilters": [
"arm64-v8a",
"x86_64"
"arm64-v8a"
]
}
}
}
],
"buildModeSet": [
{
"name": "debug",
"name": "debug"
},
{
"name": "release"
Expand All @@ -85,7 +82,6 @@
"targets": [
{
"name": "default",
// 入口模块的产品信息配置
"applyToProducts": [
"default",
"release"
Expand All @@ -95,11 +91,11 @@
},
{
"name": "proxy_core",
"srcPath": "./proxy_core",
"srcPath": "./proxy_core"
},
{
"name": "xb_components",
"srcPath": "./xb_components"
}
]
}
}
38 changes: 38 additions & 0 deletions docs/ci-hap-signing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Cloud HAP signing

The GitHub Actions workflow always produces a signed HAP:

- With no signing secrets, it uses the public OpenHarmony SDK test key. That
package is intended for OpenHarmony development devices/images that trust the
public test key.
- When all six secrets below are present, it uses the supplied Huawei developer
material and produces a package suitable for the devices covered by that
signing profile.

Configure these repository Actions secrets:

| Secret | Value |
| --- | --- |
| `HAP_SIGNING_P12_B64` | Base64-encoded `.p12` keystore |
| `HAP_SIGNING_CERT_B64` | Base64-encoded application `.cer` certificate |
| `HAP_SIGNING_PROFILE_B64` | Base64-encoded signed `.p7b` profile |
| `HAP_KEY_ALIAS` | Keystore key alias |
| `HAP_KEY_PASSWORD` | Private-key password |
| `HAP_STORE_PASSWORD` | Keystore password |

All six values must be configured together. A partial configuration deliberately
fails instead of silently publishing a test-signed package.

# Unsigned release HAP

A separate workflow, `release-hap.yml`, builds the core from the same pinned
revisions and publishes the **unsigned** release HAP as a GitHub Release:

- Pushing a version tag (`1.2.3`, `1.7.4-lts-stable.1`, ...) creates a stable
release named after the tag.
- A manual `workflow_dispatch` run refreshes the rolling `nightly` prerelease.

The release notes and `INSTALLATION-NOTES.txt` in each release explain that the
package is unsigned. An unsigned HAP cannot be installed on commercial
HarmonyOS devices: re-sign it in DevEco Studio or with `hap-sign-tool` (the
`scripts/ci/sign-hap.sh` flow above) before installing.
Loading