Hi,
I’ve been following the root access PR and issue, and took a stab at spec’ing out a modernized root access implementation using topjohnwu’s libsu. I use a lot of AYANEO emulation handhelds, and unlike Retroid/AYN devices, where access to Android/data folders seems to (magically) work without rooting the device, that is not the case for AYANEO devices. I’ve been using the older 2.0.11.2 build that has the old libsuperuser build (or the PR builds), but I’d like to have a proper root implementation again.
I wanted to present the spec here for alignment, before I went ahead and implemented it (so I don’t waste my time or yours building something that isn’t desired).
The design modernizes Syncthing Android’s root support around topjohnwu/libsu 6.0.0, replacing the older libsuperuser/raw-su approach from PR #107. The user-facing setting remains a checkbox in Settings → Behavior named “Run Syncthing as Superuser.” It is always visible, but root authorization is only requested when the user enables it. Enablement succeeds only after the app proves the privileged process is actually running as UID 0; otherwise the setting remains off and existing normal-mode Syncthing operation is left unchanged. Shizuku is intentionally excluded because its normal ADB-backed mode runs as the shell user rather than root and therefore does not satisfy the feature’s semantics.
Architecturally, the existing SyncthingService remains the normal-UID lifecycle authority, while a new non-exported SyncthingSuperuserService based on libsu RootService owns privileged execution. Communication crosses a narrow AIDL/Binder capability boundary rather than exposing arbitrary root shell execution. When superuser mode is enabled, all bundled Syncthing core invocations—serve, device ID, config generation, database reset, and delta-index reset—run through that root boundary. Privileged helpers are similarly limited to operations whose behavior must match Syncthing’s effective access, such as folder write probes, conflict discovery, app-private state access, lifecycle recovery, and ownership/SELinux repair.
The mode is deliberately fail-closed. If superuser mode is configured but root later becomes unavailable, Syncthing must not silently fall back to the app UID; the service enters a dedicated SUPERUSER_UNAVAILABLE state while preserving the enabled preference. Binder/service loss is treated as loss of supervision, not proof that the root Syncthing process exited. Recovery verifies a possible orphan using PID, /proc process start time, and expected executable before terminating it and starting a fresh supervised instance; ambiguous processes are never killed. Normal shutdown remains REST-first, with direct signaling of the specifically owned process only as a fallback.
Disabling superuser mode also fails closed: stop the root core, repair only root-owned/root-modified entries under Syncthing’s app-private filesDir, restore SELinux contexts, verify the normal app UID can access critical state, and only then clear the preference and restart normally. User sync folders are never recursively chowned or relabeled. The design also keeps root enablement out of backup/import, adds typed privilege/runtime errors and structured status reporting, and requires both unit/Binder/UI coverage and physical-device qualification for real root-manager, SELinux, /proc, revoke/recovery, and disable-repair behavior.