@@ -274,13 +274,16 @@ Responsibilities are deliberately split:
274274- ` core/ ` and ` services/helper/ ` remain source owners; ` libclash/ ` and Android ` jniLibs ` /header directories are generated
275275 output locations.
276276- ` setup.dart ` remains the release/package orchestrator and does not pre-build
277- platform artifacts or pass Core integrity data into Dart.
277+ platform artifacts or use ` dart-define ` for Core integrity data. The Windows
278+ build tool writes the runtime ` manifest.json ` beside the Core output, and the
279+ Windows bundle copies it beside the application executable.
278280
279281Platform outputs remain explicit:
280282
281283- Android builds the Go core as ` c-shared ` , then copies ` libclash.so ` and generated headers into the ` :core ` Android module.
282284- macOS and Linux build a standalone ` FlClashCore ` process used by the desktop socket integration.
283- - Windows builds ` FlClashCore.exe ` plus the Rust ` FlClashHelperService.exe ` privileged helper.
285+ - Windows builds ` FlClashCore.exe ` , the Rust ` FlClashHelperService.exe ` privileged helper, and a
286+ ` manifest.json ` containing only ` coreSha256 ` .
284287
285288The hooks follow rust_api/Cargokit's phony-output scheduling pattern, but setup uses its own cache because it builds both a
286289Go core and, on Windows, a separate Rust helper. Per-target records live under ` .dart_tool/setup_build_cache/v1/ ` :
@@ -303,11 +306,23 @@ Windows helper integrity/version check:
303306
304307- The build tool constructs the Core first, calculates its SHA256, and always
305308 builds the Rust Helper with release hardening and that expected hash.
306- - Flutter does not embed or send the Core SHA256 . Debug, Profile, and Release
309+ - Flutter reads the Core SHA256 from the bundled ` manifest.json ` and sends it with ` /ping ` . Debug, Profile, and Release
307310 builds use the same Helper protocol and may use TUN through the same flow.
308- - ` /ping ` is loopback-only and requires no request token. The Helper verifies the fixed ` FlClashCore.exe ` beside it against
309- its embedded SHA256 before reporting readiness, and repeats verification before every launch. The response includes the
310- running Helper path and protocol header; Dart checks both against the current installation.
311+ - ` /ping ` is loopback-only and requires no request token. The Helper compares the requested SHA256 with its embedded value
312+ and checks that the fixed ` FlClashCore.exe ` beside it exists; ` /start ` performs the actual Core SHA256 verification before
313+ every launch. The response includes the running Helper path and protocol header; Dart checks both against the current
314+ installation. The launcher selects the Helper only when ` /ping ` reports ready; any other readiness (missing manifest,
315+ unavailable Helper, or a Helper built for a different Core) falls back to the direct Core without requesting elevation.
316+ If ` /start ` reports a pre-spawn failure — ` coreVerificationFailed ` (the on-disk Core no longer matches the SHA the
317+ Helper and manifest agree on) or ` processLaunchFailed ` (the Core process could not be spawned) — the launcher degrades
318+ to the direct Core rather than failing the launch. ` /start ` releases the previously managed Core before it verifies,
319+ so the Helper owns no Core when either code is reported and the direct retry cannot race a Helper-managed Core.
320+ A mismatched Helper is reinstalled through the explicit TUN authorization flow, not at startup.
321+ - TUN is not a required run condition. A direct Core runs unelevated and cannot bring up TUN, so any degrade to the
322+ direct Core — an unready Helper at resolve time, or a pre-spawn ` /start ` failure — silently drops TUN and keeps the
323+ Core running. Degrading is preferred over failing the launch: an unverified Core carries no privilege the direct
324+ launch path did not already have. ` manifestMissing ` is the one readiness that is surfaced to the user, because it
325+ means the installation itself is incomplete.
311326- Flutter creates a 128-bit lowercase-hex session ID and uses it as the random named-pipe suffix. ` /start ` receives only
312327 that address and session ID, validates the fixed ` FlClashCore_<session> ` namespace, starts the fixed Core beside the
313328 Helper, and returns the same session ID plus the spawned PID. Flutter verifies both the session and named-pipe peer PID.
@@ -350,13 +365,16 @@ The helper owns its Windows Service Control Manager lifecycle through two elevat
350365The Dart layer only launches the helper's ` install ` command through ` ShellExecuteW ` ; it does not compose ` sc.exe ` ,
351366` taskkill ` , or ` cmd.exe ` command lines.
352367
353- In every Flutter build mode it opens the fixed Core executable beside the Helper without write/delete sharing, validates
354- it against the SHA256 embedded only in the Helper, and keeps that handle open through process creation. Protocol version 5
355- uses 32-character lowercase-hex session ownership:
368+ In every Flutter build mode ` /start ` opens the fixed Core executable beside the Helper without write/delete sharing,
369+ validates it against the SHA256 embedded only in the Helper, and keeps that handle open through process creation.
370+ ` /ping ` only compares the requested ` coreSha256 ` with the Helper's embedded value and checks the fixed Core path exists;
371+ it never hashes the Core. Protocol version 6 uses 32-character lowercase-hex session ownership:
356372
357- - ` GET /ping ` verifies Core and returns the current Helper executable path with ` x-flclash-helper-protocol ` .
358- - ` POST /start ` rejects unknown JSON fields, validates ` {address, sessionId} ` , replaces any previously managed Core, and
359- returns ` {sessionId, pid} ` .
373+ - ` GET /ping?coreSha256=... ` returns the current Helper executable path with ` x-flclash-helper-protocol ` when the
374+ requested SHA matches.
375+ - ` POST /start ` rejects unknown JSON fields, validates ` {address, sessionId} ` , then releases any previously managed Core
376+ before verifying the Core — so every outcome, including a rejected one, leaves the Helper owning no Core — and returns
377+ ` {sessionId, pid} ` .
360378- ` POST /stop ` validates ` {sessionId} ` and only stops the matching managed Core. A session mismatch is HTTP 409.
361379- ` GET /logs ` exposes the bounded recent Helper/Core stderr buffer with ` no-store ` caching.
362380
0 commit comments