- Detect LSPosed in all known module paths (zygisk_vector, zygisk_lsposed, lsposed)
- Skip LSPosed config warnings when hooks are already active at runtime
- Check all modules for empty targets, not just LSPosed
- Bump version to v0.5.1
Dashboard as new landing screen with module status cards (kmod, zygisk,
LSPosed), aggregated protection checks (native + Java API), and issue
alerts. Uses sealed types for type-safe state modeling (invalid states
are unrepresentable).
Three separate target lists: kmod, zygisk, and lsposed each have
independent targets.txt. Users can configure per-app which layers
protect it (L/K/Z chips in app picker). Service.sh scripts decoupled —
each resolves only its own component, with migration from unified lists.
HookEntry writes /data/system/vpnhide_hook_active with version and
boot_id so the app can detect if LSPosed hooks are active this boot.
VPN Hide app auto-adds itself to all target lists for self-diagnostics.
If just added, shows "restart needed" instead of stale check results.
App hides itself from the app picker and target counts.
Diagnostics tab no longer runs checks without VPN — shows banner only.
Removed "Run All" button (results are cached per process lifetime).
Splash screen follows system dark/light theme via Material3 DayNight.
The VPN Hide app is now the sole UI for target management. WebUI was
KernelSU-Next-only and redundant since the app works on both KSU and
Magisk. Remove webroot/, action.sh, and all references across docs,
install scripts, module descriptions, and code comments.
- WebUI: validate package names against [a-zA-Z0-9_.\-]+ before
interpolating into shell commands (both kmod and zygisk copies)
- zygisk hooks.rs: use RTM_NEWLINK/RTM_NEWADDR from filter.rs instead
of magic constants 16/20
- zygisk lib.rs: read /proc/self/maps via raw libc::open in
scrub_shadowhook_maps to bypass our own hooked_openat
- kmod: add comment explaining why seq->buf access without seq->lock
is safe in fib_route_ret (seq_read holds the mutex around ->show())
- kmod: add comment clarifying MODULE_LICENSE("GPL") vs MIT SPDX
Single VERSION file in repo root as the source of truth. The script
update-version.sh propagates it to all 5 locations: kmod module.prop,
zygisk module.prop, zygisk Cargo.toml, lsposed build.gradle.kts,
test-app build.gradle.kts. versionCode = major*10000 + minor*100 + patch.
The shell variable $UIDS accumulated UIDs with literal \n (two chars)
instead of actual newlines. printf "$UIDS" wrote garbage to
/proc/vpnhide_targets and vpnhide_uids.txt. The empty-targets case
used bare > redirect which never triggers the proc write handler.
Fix: accumulate UIDs with real newlines (like service.sh does), use
echo "$UIDS" for output, and echo (writes \n) for the empty case so
the kmod write handler fires and clears the UID list.
Affects: APK target picker, kmod WebUI, zygisk WebUI.
Unified repository for the complete Android VPN-hiding stack:
- zygisk/ — Rust Zygisk module (inline libc hooks via shadowhook)
- lsposed/ — Kotlin LSPosed module (Java API + system_server hooks)
- kmod/ — C kernel module (kretprobe hooks, invisible to anti-tamper)
CI workflows use path filters to build only the changed component.