RKNnoVPN/docs/architecture-reset/CODE_MIGRATION_PLAN.md
loop-uh 2ccfe8ce0a
Some checks failed
RKNnoVPN Linux and Android CI / policy-and-go (push) Successful in 14s
RKNnoVPN Linux and Android CI / android-debug (push) Successful in 2m5s
Full build and Forgejo release / Privacy Lint (push) Successful in 3s
Full build and Forgejo release / Local-first Runtime Guardrail (push) Successful in 2s
Full build and Forgejo release / Go Tests (push) Successful in 13s
Full build and Forgejo release / Resolve sing-box release (push) Successful in 1s
Full build and Forgejo release / Resolve Xray-core release (push) Successful in 1s
Full build and Forgejo release / Android Guardrails & Tests (push) Failing after 16s
Full build and Forgejo release / Build APK (push) Has been skipped
Full build and Forgejo release / Build Runtime CLI (arm64) (push) Successful in 11s
Full build and Forgejo release / Build Runtime CLI (armv7) (push) Successful in 11s
Full build and Forgejo release / Build sing-box (arm64) (push) Failing after 2s
Full build and Forgejo release / Build sing-box (armv7) (push) Failing after 2s
Full build and Forgejo release / Build Xray-core (arm64) (push) Failing after 3s
Full build and Forgejo release / Build Xray-core (armv7) (push) Failing after 2s
Full build and Forgejo release / Build Magisk Module (push) Has been skipped
Full build and Forgejo release / Create Release (push) Has been skipped
Перенести выпуск RKNnoVPN на Forgejo
2026-08-07 03:08:36 +03:00

6.2 KiB

RKNnoVPN Local-First Code Migration Plan

This plan is the guardrail for the daemon-to-local-first migration. The goal is to keep the existing UI and user-visible features while changing ownership and execution boundaries:

  • APK owns profile, settings, subscriptions, and update cache.
  • APK performs subscription and release network requests directly.
  • Root/module runtime is only an executor for apply/start/stop/restart/reset, logs, and module staging.
  • Old daemon IPC compatibility is not part of the active APK or runtime path.

Non-Breaking Feature Contract

The migration must preserve:

  • direct link import;
  • QR/import sheet;
  • subscriptions;
  • app list and package picker;
  • routing modes;
  • bypassRussia;
  • DNS settings;
  • always-direct apps;
  • sharing/tethering settings;
  • update check and download;
  • start, stop, restart, reset;
  • logs;
  • node tests.

Backend ownership changes are expected:

  • daemon profile get/apply/import calls become APK-local profile operations: AppProfileStore is dumb persistence, while ProfileRepository is the only owner/publisher of observable profile state;
  • daemon subscription preview/refresh becomes APK-side subscription fetching and parsing through ApkSubscriptionClient;
  • daemon update check/download becomes APK-side Forgejo release fetching through ApkReleaseUpdater;
  • daemon backend controls become explicit root CLI commands through RuntimeController;
  • daemon logs become RuntimeController.logs.

Phase 1: APK Boot Without Runtime

Required code:

  • app/app/src/main/AndroidManifest.xml
  • app/app/src/main/kotlin/com/rknnovpn/panel/profile/AppProfile.kt
  • app/app/src/main/kotlin/com/rknnovpn/panel/storage/AppProfileStore.kt
  • app/app/src/main/kotlin/com/rknnovpn/panel/storage/FileAppProfileStore.kt
  • app/app/src/main/kotlin/com/rknnovpn/panel/repository/ProfileRepository.kt

Acceptance:

  • APK opens with no module installed.
  • APK profile is created and stored under app-private storage.
  • Profile editing and imports do not require root or runtime compatibility.
  • AppProfileStore remains a persistence-only contract: no StateFlow, no long-lived profile, warning, or error properties, and no UI/runtime state ownership.
  • ProfileRepository is the single owner of profile-related observable state: profile, freshness/loading, error, and notice flows are published from the repository, not from storage.

Phase 2: Remove Active IPC Compatibility

Forbidden in active code paths:

  • compat.check
  • ipc.contract
  • GeneratedDaemonContract
  • requireCompatible
  • compatibilityCache
  • repair cooldowns
  • stable invalid response recovery
  • automatic module repair after parse errors
  • daemon-owned profile as APK startup dependency

Acceptance:

  • Forbidden strings are absent from app, runtime, module, tools, and CI scripts except for the local-first checker that defines the forbidden patterns.

Phase 3: APK-Side Subscriptions

Required code:

  • app/app/src/main/kotlin/com/rknnovpn/panel/subscription/ApkSubscriptionClient.kt
  • app/app/src/main/kotlin/com/rknnovpn/panel/subscription/SubscriptionRepository.kt

Acceptance:

  • APK downloads subscription URLs itself.
  • APK parses payloads with LinkParser.
  • APK saves imported nodes into AppProfileStore.
  • Runtime/module absence does not block preview, commit, refresh, or refresh all.

Phase 4: APK-Side Updates

Required code:

  • app/app/src/main/kotlin/com/rknnovpn/panel/update/ApkReleaseUpdater.kt
  • app/app/src/main/kotlin/com/rknnovpn/panel/update/UpdateRepository.kt
  • settings update flow.

Acceptance:

  • APK checks the latest release in the project Forgejo itself.
  • APK downloads module.zip, panel.apk, and SHA256SUMS.
  • APK verifies SHA256 before exposing downloaded update paths.
  • Runtime is only needed to stage/install the root/module part.

Phase 5: Root CLI Runtime

Required code:

  • app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeController.kt
  • app/app/src/main/kotlin/com/rknnovpn/panel/root/RootCommandExecutor.kt
  • runtime/cmd/rknnovpn-runtime/main.go

Required commands:

  • rknnovpn-runtime version
  • rknnovpn-runtime status
  • rknnovpn-runtime apply <profile.json>
  • rknnovpn-runtime start
  • rknnovpn-runtime stop
  • rknnovpn-runtime restart
  • rknnovpn-runtime reset
  • rknnovpn-runtime logs
  • rknnovpn-runtime stage-module <module.zip>

Runtime actions are enabled only when:

  • runtime release version equals APK BuildConfig.VERSION_NAME;
  • runtime version code equals APK BuildConfig.VERSION_CODE;
  • runtime wireEpoch equals 1.

If runtime is missing, old, root-denied, or returns malformed JSON, APK local features remain usable and runtime controls fail as disabled/unavailable actions.

Phase 6: Runtime Profile Renderer

Required code:

  • app/app/src/main/kotlin/com/rknnovpn/panel/runtime/RuntimeProfileRenderer.kt

Acceptance:

  • APK profile is converted into runtime config only during apply/start.
  • APK boot does not depend on runtime config schema.
  • Routing, DNS, sharing, always-direct apps, app groups, bypassRussia, and active node selection are carried into the runtime projection.
  • Runtime projection does not carry APK-only subscription registry, share-link fallback data, or stale node metadata.

Phase 7: Tests And Validation

Expected coverage areas:

  • APK profile file store behavior.
  • Profile repository local-first behavior.
  • Profile store/repository boundary: store returns operation results only, repository publishes profile/error/notice state.
  • Subscription payload parsing and repository behavior.
  • Release metadata/checksum parsing and update repository behavior.
  • Runtime availability/protocol handling for missing, mismatched, and malformed runtime responses.
  • Runtime profile rendering for routing, DNS, sharing, always-direct, app groups, and stale-node filtering.

Required validation commands:

./gradlew :app:assembleDebug
./gradlew :app:testDebugUnitTest
scripts/ci/check_local_first_runtime.sh
scripts/ci/check_apk_privacy.sh
rg "compat\\.check|ipc\\.contract|GeneratedDaemonContract|requireCompatible|compatibilityCache|Repair cooldown|stableInvalidResponse" app runtime module .forgejo

Release APK privacy validation requires a built APK:

scripts/ci/check_release_apk_privacy.sh path/to/app.apk