Digital Garden
The Non-Pro Cage: The Auto-Reopening System Proxy
Some cages have no door, only an invisible lock — you think you cannot find the switch in settings, but the program never intended to let you turn it off.
On the afternoon of October 4, a long-standing ghost requirement was finally dissected: why do the system proxy and TUN always auto-start together, when settings clearly have no “auto-enable system proxy” option?
I. The config file’s innocence
The engineers’ first stop was the config directory: in appconfig.yaml, system_proxy: true and tun_mode: true are indeed two independently persisted switches — it looked like merely “last state saved.” But the modification timestamp gave it away: 11:52, just written back. The state was not lying in the file; something was changing it back in real time.
The real logic lives not in the config file but in the program’s body. app.asar unpacked to only a 16KB launcher shell; the logic is in the Go backend and — the most critical link — the embedded dashboard frontend.
II. The frontend’s perpetual motion machine
Pulling the frontend bundle from the running service, the culprit useEffect finally emerged:
Non-pro mode + kernel running + system proxy off → automatically calls enable on the system proxy — unless localStorage has a “user manually turned off” marker (
ninja_non_pro_proxy_optout).
It seemed to leave a back door. But another useEffect deletes that marker on every kernel start/stop — so the marker never survives to the next boot. Every boot night, restart’s janitor erases yesterday’s “do not disturb,” and dawn’s custody machine resumes. This is not a bug; it is forced custody by design in non-pro mode.
III. The only key
There is only one way out: Pro Mode (pro_mode). Once enabled, the auto-enable logic’s first check simply returns — the system proxy belongs to you alone from then on. The engineers wrote pro_mode: true via the API and restarted the app simulating a boot auto-start: system proxy off, and not reopened. The diagnosis was inversely verified.
Interlude: one network outage and its lesson
Mid-fix, the engineers planted a real faceplant: trying to shut down the app gracefully, the called /api/app/shutdown actually returned unauthorized (requires an Electron internal token) — the graceful shutdown never happened — yet they force-killed all processes without verifying, backend and kernel taken away together, TUN disconnected, network down for seconds. The user’s scolding was blunt but every word fair: “for every setting, be clear about the consequences, or give yourself a terminal proxy.”
The postmortem was carved into the rules: before touching kernel/processes, state consequences first, verify each step, never kill processes directly; local check commands always hang on the 127.0.0.1:7897 terminal proxy before executing. Only after the network recovered did the fix resume — preserve the channel first, then move the sluice.
Real coordinates
- Symptom: every boot the system proxy and TUN both enable; manual turn-off reopens within seconds; no corresponding option in settings
- Root cause: embedded dashboard frontend
useEffect— non-pro + kernel running + proxy off → auto-enable; theninja_non_pro_proxy_optoutopt-out marker deleted by another useEffect at kernel start/stop, never surviving to next boot - Evidence:
appconfig.yamlwrite timestamp, embedded frontend bundle in app.asar, service-mode run, AI Plus on (initial suspect, later cleared) - Fix:
pro_mode: true(written via API) → auto-logic returns → reboot simulation verified no reopen; registry ProxyEnable=0 persistent - Incident: unverified process kill caused outage → user scolding → rules: hang terminal proxy 7897 first, state consequences first, step-by-step verify, no process kills
Comments