Core And Platform
When To Use
Use this for changes touching lib/core/, lib/manager/, core/, services/helper/, Android app/service modules, build
hooks, system proxy, tray, VPN, TUN, or platform-specific desktop/mobile behavior.
Workflow
- Identify the authoritative owner before changing behavior:
- Shared facade/protocol:
lib/core/controller.dart, lib/core/interface.dart, and lib/core/method.dart.
- Android Core connection:
lib/core/lib.dart, lib/plugins/service.dart, and Android ServicePlugin.
- Android start/stop intent:
ServiceState; binding/process-time bookkeeping: ServiceController.
- Desktop composition:
lib/core/service.dart; lifecycle/process ownership: lib/core/desktop/lifecycle.dart.
- Desktop IPC/RPC:
lib/core/desktop/transport.dart and lib/core/desktop/rpc_client.dart.
- Desktop launch ownership:
lib/core/desktop/launcher.dart; Windows Helper HTTP contract:
lib/core/desktop/helper_client.dart and services/helper/.
- Flutter orchestration:
lib/providers/actions/core.dart and system.dart; UI/event observation: lib/manager/.
- Trace every entry path into that owner, including UI/provider calls, Quick Settings, notification actions, Always-on VPN,
revoke callbacks, application exit, and crash/disconnect recovery. Lifecycle callbacks are not implicit user intent.
- Preserve latest-intent semantics:
- Desktop revisions converge to running/restarted/stopped/closed and report applied/coalesced/superseded outcomes.
- Android Flutter calls stay optimistic;
ServiceState identity-checks the latest native RunRequest.
- Route feature calls through
CoreController and CoreHandlerInterface. Do not bypass desktop process leases or create a
second Android service binding owner.
- Keep JSON envelopes and event shapes identical across Dart, Go, JNI, and desktop IPC. If event traffic changes, preserve
the separate priority and bulk queues in
core/message.go.
- Keep shutdown single-owned and terminal.
SystemExitCoordinator sequences resource cleanup, window close, Core close,
and process exit; widget/manager disposal must not race it.
- Add or update focused tests at the narrowest layer, then run the matching commands from
.agents/commands.md:
- Desktop lifecycle/transport/RPC:
test/core/desktop/ plus test/core/service_test.dart.
- Cross-language envelopes/events:
test/core/protocol_contract_test.dart and CGO_ENABLED=0 go test ..
- Provider/exit convergence:
test/providers/action_test.dart and test/providers/system_action_test.dart.
- Android Kotlin: compile each touched Gradle module with JDK 17.
- Windows Helper: Cargo format/tests; run the
windows-service feature on Windows.
- Explicitly state host gaps. Always-on VPN, VPN permission, system revoke, named-pipe peer identity, and Windows Service
Control Manager behavior need their real platform even when portable tests pass.
Reference Files
Read .agents/architecture.md for the current core modes, manager stack, build hooks, local plugins, and Windows helper notes.
Pitfalls
- Keep the Windows Helper protocol and Core SHA256 validation identical across
Flutter build modes; the Helper owns executable integrity checks.
- Protocol version 6 uses a 32-character lowercase-hex session ID.
/start must return the submitted session and PID;
/stop must never terminate a different session; Dart must verify the connected named-pipe peer PID.
/start must release the previously managed Core before it verifies, so no /start outcome leaves a Helper-managed
Core behind for the caller's direct-launch fallback to race.
- The Helper owns a managed Core until its exit is confirmed. A
200 from /stop means the Core is gone; when
termination cannot be confirmed the Helper keeps the child and answers coreStopFailed, and /start reports the same
code instead of spawning a replacement. Keep that code out of the Dart pre-spawn fallback set in
helper_client.dart, or the direct launch will race a Core the Helper still owns.
- TUN is not a required run condition. Degrading to the unelevated direct Core — and silently losing TUN — is the
expected outcome whenever the Helper path fails; do not fail the launch instead.
- A desktop process lease with unconfirmed exit must remain owned until cleanup succeeds. Do not discard it and start a
replacement Core.
CoreController.close() is terminal. Do not call it from a reusable manager lifecycle or recover by starting it again.
ServiceBroadcastReceiver.goAsync() must finish once even on timeout; its watchdog releases the broadcast only and must
not become a service timeout.
- Do not interpret service creation/destruction as start/stop intent. Always-on startup is explicit through
VPN_START_REQUESTED; revoke is explicit through VPN_REVOKED.
- Keep log/request floods from evicting state-bearing Core events. Each queue may evict only its own oldest item.
- Do not expose direct filesystem deletion APIs through Core or helper IPC; use
a scope-specific cleanup API instead.
plugins/setup/ is a build harness, not a Dart API plugin.
- Build hooks can trigger Go or Rust compilation indirectly through Flutter platform builds.
1---2name: core-platform3description: Use when changing FlClash Core integration, lifecycle/process ownership, Go event delivery, Android services, desktop IPC, platform managers, VPN/TUN, or Windows Helper flow.4---56# Core And Platform78## When To Use910Use this for changes touching `lib/core/`, `lib/manager/`, `core/`, `services/helper/`, Android app/service modules, build11hooks, system proxy, tray, VPN, TUN, or platform-specific desktop/mobile behavior.1213## Workflow14151. Identify the authoritative owner before changing behavior:16 - Shared facade/protocol: `lib/core/controller.dart`, `lib/core/interface.dart`, and `lib/core/method.dart`.17 - Android Core connection: `lib/core/lib.dart`, `lib/plugins/service.dart`, and Android `ServicePlugin`.18 - Android start/stop intent: `ServiceState`; binding/process-time bookkeeping: `ServiceController`.19 - Desktop composition: `lib/core/service.dart`; lifecycle/process ownership: `lib/core/desktop/lifecycle.dart`.20 - Desktop IPC/RPC: `lib/core/desktop/transport.dart` and `lib/core/desktop/rpc_client.dart`.21 - Desktop launch ownership: `lib/core/desktop/launcher.dart`; Windows Helper HTTP contract:22 `lib/core/desktop/helper_client.dart` and `services/helper/`.23 - Flutter orchestration: `lib/providers/actions/core.dart` and `system.dart`; UI/event observation: `lib/manager/`.242. Trace every entry path into that owner, including UI/provider calls, Quick Settings, notification actions, Always-on VPN,25 revoke callbacks, application exit, and crash/disconnect recovery. Lifecycle callbacks are not implicit user intent.263. Preserve latest-intent semantics:27 - Desktop revisions converge to running/restarted/stopped/closed and report applied/coalesced/superseded outcomes.28 - Android Flutter calls stay optimistic; `ServiceState` identity-checks the latest native `RunRequest`.294. Route feature calls through `CoreController` and `CoreHandlerInterface`. Do not bypass desktop process leases or create a30 second Android service binding owner.315. Keep JSON envelopes and event shapes identical across Dart, Go, JNI, and desktop IPC. If event traffic changes, preserve32 the separate priority and bulk queues in `core/message.go`.336. Keep shutdown single-owned and terminal. `SystemExitCoordinator` sequences resource cleanup, window close, Core close,34 and process exit; widget/manager disposal must not race it.357. Add or update focused tests at the narrowest layer, then run the matching commands from `.agents/commands.md`:36 - Desktop lifecycle/transport/RPC: `test/core/desktop/` plus `test/core/service_test.dart`.37 - Cross-language envelopes/events: `test/core/protocol_contract_test.dart` and `CGO_ENABLED=0 go test .`.38 - Provider/exit convergence: `test/providers/action_test.dart` and `test/providers/system_action_test.dart`.39 - Android Kotlin: compile each touched Gradle module with JDK 17.40 - Windows Helper: Cargo format/tests; run the `windows-service` feature on Windows.418. Explicitly state host gaps. Always-on VPN, VPN permission, system revoke, named-pipe peer identity, and Windows Service42 Control Manager behavior need their real platform even when portable tests pass.4344## Reference Files4546Read `.agents/architecture.md` for the current core modes, manager stack, build hooks, local plugins, and Windows helper notes.4748## Pitfalls4950- Keep the Windows Helper protocol and Core SHA256 validation identical across51 Flutter build modes; the Helper owns executable integrity checks.52- Protocol version 6 uses a 32-character lowercase-hex session ID. `/start` must return the submitted session and PID;53 `/stop` must never terminate a different session; Dart must verify the connected named-pipe peer PID.54- `/start` must release the previously managed Core before it verifies, so no `/start` outcome leaves a Helper-managed55 Core behind for the caller's direct-launch fallback to race.56- The Helper owns a managed Core until its exit is confirmed. A `200` from `/stop` means the Core is gone; when57 termination cannot be confirmed the Helper keeps the child and answers `coreStopFailed`, and `/start` reports the same58 code instead of spawning a replacement. Keep that code out of the Dart pre-spawn fallback set in59 `helper_client.dart`, or the direct launch will race a Core the Helper still owns.60- TUN is not a required run condition. Degrading to the unelevated direct Core — and silently losing TUN — is the61 expected outcome whenever the Helper path fails; do not fail the launch instead.62- A desktop process lease with unconfirmed exit must remain owned until cleanup succeeds. Do not discard it and start a63 replacement Core.64- `CoreController.close()` is terminal. Do not call it from a reusable manager lifecycle or recover by starting it again.65- `ServiceBroadcastReceiver.goAsync()` must finish once even on timeout; its watchdog releases the broadcast only and must66 not become a service timeout.67- Do not interpret service creation/destruction as start/stop intent. Always-on startup is explicit through68 `VPN_START_REQUESTED`; revoke is explicit through `VPN_REVOKED`.69- Keep log/request floods from evicting state-bearing Core events. Each queue may evict only its own oldest item.70- Do not expose direct filesystem deletion APIs through Core or helper IPC; use71 a scope-specific cleanup API instead.72- `plugins/setup/` is a build harness, not a Dart API plugin.73- Build hooks can trigger Go or Rust compilation indirectly through Flutter platform builds.