476 lines
18 KiB
Markdown
476 lines
18 KiB
Markdown
# Kylin Remote Control & Monitoring Setup
|
||
|
||
Complete reference for controlling and monitoring a Kylin Linux machine from a
|
||
Windows 11 PC over a local phone-hotspot network. No VPN is used for the SSH
|
||
connection itself — only the local Wi-Fi/hotspot.
|
||
|
||
---
|
||
|
||
## 0. Bootstrap — START HERE in a new session
|
||
|
||
**The connection is already fully set up** (SSH key login + passwordless sudo).
|
||
A new session does **NOT** need to repeat the setup in §2–§3 — those sections are
|
||
records of what was already done. Just connect and go.
|
||
|
||
**Verify everything with one command** (run from the Windows machine, e.g. Claude Code's Bash tool):
|
||
|
||
```bash
|
||
ssh -o BatchMode=yes -o ConnectTimeout=10 kylin 'echo SSH_OK; whoami; hostname; sudo -n true && echo SUDO_OK'
|
||
```
|
||
|
||
Expected: `SSH_OK` / `caichl` / `caichl-HUAWEIQINGYUNL420` / `SUDO_OK`.
|
||
|
||
- ✅ **`SSH_OK` + `SUDO_OK`** → full control. Run anything (incl. `sudo`) via
|
||
`ssh -o BatchMode=yes kylin "<command>"`. No password needed.
|
||
- ❌ **asks for password / times out** → most likely the **hotspot IP changed**
|
||
(`192.168.43.248` is DHCP-assigned and can change between sessions). Ask the user
|
||
to re-check the Kylin IP (`hostname -I` on that machine), update
|
||
`C:\Users\adusu\.ssh\config`, then re-verify. If it asks for a password, the key
|
||
may have been removed — see §2.3 / §2.4 to re-establish.
|
||
- ℹ️ SSH prints a harmless **post-quantum warning** to stderr — ignore it (the
|
||
examples in this doc filter it out).
|
||
|
||
**Connect manually:** `ssh kylin` (alias in `C:\Users\adusu\.ssh\config` → `caichl@192.168.43.248`,
|
||
with the pinned cipher/MAC already configured — see §2).
|
||
|
||
> A fresh Claude Code session also auto-loads memory pointers (`MEMORY.md` →
|
||
> `kylin-remote-machine`, `kylin-command-logging`, `kylin-mihomo-vpn`) that link
|
||
> back here. This file is the authoritative, self-contained reference.
|
||
|
||
---
|
||
|
||
## 1. The two machines
|
||
|
||
| Role | Details |
|
||
|------|---------|
|
||
| **Controller** | Windows 11 (`C:\Users\adusu`), where Claude Code + VPN run |
|
||
| **Target** | Kylin V10 SP1, Huawei QINGYUN L420, **ARM64 / aarch64** |
|
||
| **Network** | Shared phone hotspot, subnet `192.168.43.x` |
|
||
| **Target IP** | `192.168.43.248` |
|
||
| **Target user** | `caichl` |
|
||
|
||
> **Key idea:** Claude Code needs the VPN to reach Anthropic's servers, but the
|
||
> SSH hop from Windows → Kylin is **local LAN traffic** and needs no VPN. The two
|
||
> connections are independent.
|
||
|
||
---
|
||
|
||
## 2. SSH connection
|
||
|
||
### 2.1 Required cipher/MAC (important)
|
||
|
||
The default cipher fails over this hotspot link with
|
||
`Corrupted MAC on input`. The connection must pin a specific cipher and MAC:
|
||
|
||
```
|
||
-c aes256-ctr -m hmac-sha2-256
|
||
```
|
||
|
||
### 2.2 Windows SSH config
|
||
|
||
File: `C:\Users\adusu\.ssh\config`
|
||
|
||
```sshconfig
|
||
Host kylin kyln
|
||
HostName 192.168.43.248
|
||
User caichl
|
||
Ciphers aes256-ctr
|
||
MACs hmac-sha2-256
|
||
```
|
||
|
||
With this in place, simply run:
|
||
|
||
```powershell
|
||
ssh kylin # or: ssh kyln (both aliases work)
|
||
```
|
||
|
||
The equivalent explicit command (no config) is:
|
||
|
||
```powershell
|
||
ssh -m hmac-sha2-256 -c aes256-ctr caichl@192.168.43.248
|
||
```
|
||
|
||
### 2.3 Passwordless login (SSH key)
|
||
|
||
The Windows key `~/.ssh/id_ed25519.pub` was copied to the Kylin machine:
|
||
|
||
```powershell
|
||
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | `
|
||
ssh kylin "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||
```
|
||
|
||
Result: `ssh kylin` logs in with **no password**.
|
||
|
||
### 2.4 Passwordless sudo
|
||
|
||
So admin commands can run non-interactively from Windows:
|
||
|
||
```bash
|
||
# Run once on the Kylin machine (asks for password the one time)
|
||
sudo bash -c 'echo "caichl ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/caichl-nopasswd; \
|
||
chmod 440 /etc/sudoers.d/caichl-nopasswd; \
|
||
visudo -cf /etc/sudoers.d/caichl-nopasswd'
|
||
```
|
||
|
||
> **Security note:** This makes `caichl` a passwordless-sudo account — acceptable
|
||
> for a disposable machine only. To revert: `sudo rm /etc/sudoers.d/caichl-nopasswd`.
|
||
|
||
After this, from Windows you can run, e.g.:
|
||
|
||
```powershell
|
||
ssh -o BatchMode=yes kylin "sudo systemctl status ssh"
|
||
```
|
||
|
||
### 2.5 Account credentials (recovery only)
|
||
|
||
> ⚠️ **Plaintext password below — keep this file private; do NOT commit or share it.**
|
||
|
||
| Item | Value |
|
||
|------|-------|
|
||
| User | `caichl` |
|
||
| Password | `Caicai@2026` |
|
||
|
||
**Normally not needed** — SSH login uses the key (§2.3) and sudo is passwordless
|
||
(§2.4). Use this only to **recover** if the key auth or sudoers drop-in is ever lost
|
||
(e.g. to log in interactively and re-copy the key, or to re-create
|
||
`/etc/sudoers.d/caichl-nopasswd`). To change it: run `passwd` on the Kylin machine.
|
||
|
||
---
|
||
|
||
## 3. Initial SSH server setup (done on the Kylin machine)
|
||
|
||
```bash
|
||
sudo apt update
|
||
sudo apt install -y openssh-server
|
||
sudo systemctl enable --now ssh
|
||
hostname -I # find the IP (192.168.43.248)
|
||
whoami # confirm the username (caichl)
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Monitoring
|
||
|
||
### 4.1 SSH logins & sessions (works out of the box)
|
||
|
||
```bash
|
||
who # who is logged in + source
|
||
w # sessions + current command
|
||
last # login history with IPs
|
||
lastb # FAILED login attempts (sudo)
|
||
sudo grep "Accepted" /var/log/auth.log # successful SSH logins
|
||
sudo grep "Failed" /var/log/auth.log # failed/suspicious attempts
|
||
sudo tail -f /var/log/auth.log # live SSH activity
|
||
```
|
||
|
||
### 4.2 Command logging — no reboot (WORKING)
|
||
|
||
Logs every command typed in an interactive shell, with user, tty, source IP,
|
||
and the command text.
|
||
|
||
**`/etc/profile.d/cmdlog.sh`**
|
||
```bash
|
||
# Log interactive shell commands to syslog (facility local6) -> /var/log/cmdlog.log
|
||
if [ -n "$BASH" ] && [ -n "$PS1" ]; then
|
||
export PROMPT_COMMAND='logger -p local6.notice -t cmdlog "user=$USER tty=$(tty 2>/dev/null) from=$(who am i 2>/dev/null | sed -n "s/.*(\(.*\)).*/\1/p") cmd=$(history 1 | sed "s/^ *[0-9]* *//")"'
|
||
fi
|
||
```
|
||
|
||
**`/etc/rsyslog.d/30-cmdlog.conf`**
|
||
```
|
||
local6.* /var/log/cmdlog.log
|
||
```
|
||
|
||
**Setup commands**
|
||
```bash
|
||
sudo touch /var/log/cmdlog.log
|
||
sudo chown syslog:adm /var/log/cmdlog.log # MUST be syslog-owned (rsyslog drops privs)
|
||
sudo chmod 640 /var/log/cmdlog.log
|
||
sudo systemctl restart rsyslog
|
||
```
|
||
|
||
**View the logs**
|
||
```bash
|
||
sudo cat /var/log/cmdlog.log # all logged commands
|
||
sudo tail -f /var/log/cmdlog.log # live
|
||
```
|
||
|
||
Example line:
|
||
```
|
||
Jun 11 10:54:22 caichl-HUAWEIQINGYUNL420 cmdlog: user=caichl tty=/dev/pts/5 from=192.168.43.180 cmd=echo hello
|
||
```
|
||
|
||
> **Gotcha:** rsyslog runs as the `syslog` user. If `/var/log/cmdlog.log` is owned
|
||
> by `root:root`, nothing is written. It must be `chown syslog:adm`.
|
||
|
||
### 4.3 System-wide auditing — auditd (QUEUED, UNVERIFIED)
|
||
|
||
`auditd` is installed and enabled, with a rule logging all `execve` for real
|
||
users:
|
||
|
||
**`/etc/audit/rules.d/cmdlog.rules`**
|
||
```
|
||
-a exit,always -F arch=b64 -S execve -F auid>=1000 -F auid!=4294967295 -k cmdlog
|
||
```
|
||
|
||
It cannot run yet because the kernel booted with `audit=0`. This was changed to
|
||
`audit=1`:
|
||
|
||
```bash
|
||
sudo sed -i 's/audit=0/audit=1/' /etc/default/grub
|
||
# /boot is read-only on this device — remount to rebuild grub
|
||
sudo mount -o remount,rw /boot
|
||
sudo update-grub
|
||
sudo mount -o remount,ro /boot
|
||
```
|
||
|
||
> **Caveat:** This device appears to boot via **Huawei firmware, not standard
|
||
> GRUB** (running kernel `-27` is not the one in `grub.cfg` which lists `-11`, and
|
||
> the kernel cmdline has Huawei-specific params). So the `audit=1` change **may
|
||
> not take effect** on reboot. The no-reboot logging in §4.2 already covers
|
||
> interactive command logging, so auditd is a bonus.
|
||
|
||
**Verify after any reboot**
|
||
```bash
|
||
cat /proc/sys/kernel/audit_enabled # want: 1
|
||
sudo systemctl is-active auditd # want: active
|
||
sudo ausearch -k cmdlog -i | tail -40 # human-readable command audit
|
||
sudo aureport -x --summary # summary of executables run
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Device-specific quirks (Huawei Kylin ARM64)
|
||
|
||
- **Architecture is ARM64 (aarch64).** Software must have an ARM64 build; x86-only
|
||
proprietary apps will not install/run.
|
||
- **`/boot` is a separate ext4 partition mounted read-only.** Remount rw to change
|
||
GRUB, then back to ro (see §4.3).
|
||
- **Boot likely controlled by Huawei firmware**, limiting effectiveness of GRUB
|
||
cmdline edits.
|
||
- **Hotspot link needs `aes256-ctr` / `hmac-sha2-256`** or SSH fails with
|
||
"Corrupted MAC on input".
|
||
|
||
---
|
||
|
||
## 6. Quick reference
|
||
|
||
```powershell
|
||
# Connect
|
||
ssh kylin
|
||
|
||
# Run a one-off command from Windows
|
||
ssh kylin "uptime"
|
||
|
||
# Run an admin command from Windows
|
||
ssh kylin "sudo systemctl status ssh"
|
||
|
||
# See logged commands
|
||
ssh kylin "sudo tail -50 /var/log/cmdlog.log"
|
||
|
||
# Watch live
|
||
ssh kylin "sudo tail -f /var/log/cmdlog.log"
|
||
|
||
# SSH login history
|
||
ssh kylin "last"
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Troubleshooting
|
||
|
||
| Symptom | Cause / Fix |
|
||
|---------|-------------|
|
||
| `Corrupted MAC on input` | Use `-c aes256-ctr -m hmac-sha2-256` (already in config) |
|
||
| `Connection timed out` | Both devices not on same hotspot, or AP/client isolation on the phone — disable isolation |
|
||
| `ssh kyln` "could not resolve" | Alias spelling — config now accepts both `kylin` and `kyln` |
|
||
| Still asks for password | SSH key not copied (§2.3) |
|
||
| `sudo` asks for password | sudoers drop-in empty/missing (§2.4) |
|
||
| `audit support not in kernel` | Kernel booted `audit=0`; needs reboot with `audit=1` (§4.3) |
|
||
| `cmdlog.log` empty | File not owned by `syslog:adm`; `sudo chown syslog:adm /var/log/cmdlog.log` then restart rsyslog |
|
||
| `grub.cfg: Read-only file system` | `/boot` is read-only; `sudo mount -o remount,rw /boot` first |
|
||
| VPN GUI app won't run (`GLIBC_2.34 not found`) | Kylin has glibc 2.31; modern Flutter/Electron clients need 2.34+. Use the Go-based mihomo core instead (see §8) |
|
||
|
||
---
|
||
|
||
## 8. VPN / proxy: mihomo + web dashboard
|
||
|
||
**Why not FlClash / GUI clients:** Kylin V10 SP1 has **glibc 2.31**, but FlClash 0.8.93
|
||
and similar modern GUI clients require **glibc 2.34+** and fail to run. Upgrading
|
||
glibc would risk breaking the OS. mihomo is a **statically-linked Go binary** with
|
||
no glibc dependency, so it runs fine — controlled via a browser dashboard.
|
||
|
||
### Current state
|
||
> **VPN is currently OFF.** The `mihomo` service is **stopped** and **disabled**
|
||
> (auto-start removed), so it stays off across reboots until re-enabled. Ports
|
||
> `7890`/`9090` are closed. The full configuration below is intact and ready to
|
||
> resume.
|
||
|
||
### Turn the VPN on / off (run on Kylin, or via `ssh kylin`)
|
||
```bash
|
||
# TURN ON (and restore auto-start on boot)
|
||
sudo systemctl enable --now mihomo
|
||
|
||
# TURN OFF for this session only (comes back on next reboot)
|
||
sudo systemctl stop mihomo
|
||
|
||
# TURN OFF and keep it off across reboots <-- current state
|
||
sudo systemctl stop mihomo && sudo systemctl disable mihomo
|
||
|
||
# Check state
|
||
systemctl is-active mihomo # active / inactive
|
||
systemctl is-enabled mihomo # enabled / disabled
|
||
```
|
||
> Reminder: if you had set the Kylin **system proxy** to `127.0.0.1:7890`, switch
|
||
> it back to "None" while the VPN is off, or apps will fail to reach the network.
|
||
|
||
### Desktop access
|
||
A launcher **"Mihomo VPN Dashboard"** was added to the app menu (Network category)
|
||
at `/usr/share/applications/mihomo-dashboard.desktop`; it opens
|
||
`http://127.0.0.1:9090/ui/`. (Only works while the service is running.)
|
||
|
||
### Installed components
|
||
| Item | Location / value |
|
||
|------|------------------|
|
||
| mihomo core | `/usr/local/bin/mihomo` (Clash.Meta v1.19.27, arm64) |
|
||
| Config | `/etc/mihomo/config.yaml` |
|
||
| Web dashboard (zashboard) | `/etc/mihomo/ui/` |
|
||
| systemd service | `/etc/systemd/system/mihomo.service` (auto-starts on boot) |
|
||
| Proxy port (HTTP+SOCKS) | `7890` |
|
||
| Dashboard API | `0.0.0.0:9090` |
|
||
| Dashboard secret | `428968aebf7c301565c6b6d3a11826c4` |
|
||
|
||
### Install steps (done; for reference / rebuild)
|
||
```bash
|
||
# binary (downloaded on Windows through VPN, scp'd over to dodge chicken-and-egg)
|
||
gunzip mihomo-linux-arm64-vX.gz
|
||
sudo install -m 755 mihomo-linux-arm64 /usr/local/bin/mihomo
|
||
# dashboard
|
||
sudo mkdir -p /etc/mihomo/ui && sudo unzip zashboard-dist.zip -d /etc/mihomo/ui
|
||
# config.yaml: mixed-port 7890, external-controller 0.0.0.0:9090, secret, external-ui: ui,
|
||
# proxy-providers (subscription URL), PROXY (select) + AUTO (url-test) groups
|
||
sudo systemctl enable --now mihomo
|
||
```
|
||
> The subscription URL lives in `/etc/mihomo/config.yaml` under `proxy-providers:`.
|
||
> `external-ui-name` must NOT be set (it makes mihomo look in a `ui/<name>` subdir
|
||
> and auto-download); serve `/etc/mihomo/ui` directly.
|
||
|
||
### Open the dashboard
|
||
- **From Windows browser:** `http://192.168.43.248:9090/ui/`
|
||
- backend/API: `http://192.168.43.248:9090` • secret: `428968aebf7c301565c6b6d3a11826c4`
|
||
- **From Kylin desktop browser:** `http://127.0.0.1:9090/ui/`
|
||
|
||
### Use the proxy
|
||
Point apps at `127.0.0.1:7890` (HTTP/SOCKS), or set the Kylin system proxy
|
||
(Settings → Network → Proxy → Manual → `127.0.0.1:7890`). For transparent
|
||
whole-system routing, enable mihomo **TUN mode** (`/dev/net/tun` is present).
|
||
|
||
### Manage via API (examples)
|
||
```bash
|
||
S=428968aebf7c301565c6b6d3a11826c4
|
||
# switch PROXY group to a node / AUTO
|
||
curl -X PUT -H "Authorization: Bearer $S" http://127.0.0.1:9090/proxies/PROXY -d '{"name":"AUTO"}'
|
||
# verify exit IP goes through the proxy
|
||
curl -x http://127.0.0.1:7890 -s https://api.ip.sb/geoip
|
||
# update subscription / reload
|
||
sudo systemctl restart mihomo
|
||
```
|
||
|
||
**Verified working:** exit IP resolved to Singapore and `google.com/generate_204`
|
||
returned HTTP 204 through the proxy.
|
||
|
||
---
|
||
|
||
## 9. Browsers
|
||
|
||
| Browser | App-menu name | Type / location | Notes |
|
||
|---------|---------------|-----------------|-------|
|
||
| **Chromium** | "Chromium" | snap `chromium` v149 (`/snap/bin/chromium`) | Installed as the Chrome/Edge equivalent — same engine, Chrome Web Store extensions work. **Set as the default browser.** |
|
||
|
||
- **Chrome & Edge cannot be installed** — neither has an official ARM64 Linux build
|
||
(same architecture wall as the VPN GUI clients). Chromium is the substitute.
|
||
- Installing the chromium snap first required **updating snapd** (Kylin shipped 2.54,
|
||
too old → error `assumes unsupported features: snapd2.55`). Fix:
|
||
`sudo snap install snapd` (brought it to 2.75.2), then `sudo snap install chromium`.
|
||
- Default browser is recorded in `~/.config/mimeapps.list`
|
||
(`text/html`, `x-scheme-handler/http(s)` → `chromium_chromium.desktop`). If a
|
||
double-click opens the wrong app, right-click the file → **Open with → Chromium →
|
||
Set as default**.
|
||
|
||
### Desktop instruction files (for the user at the machine)
|
||
- `~/桌面/VPN_Guide.html` + `~/桌面/VPN_Guide.md` (copies also in `~/Desktop/`):
|
||
a user-facing "how to turn the VPN on/off" guide. Double-click the **`.html`** to
|
||
read it rendered in a browser; the **`.md`** opens in **VS Code** (`code`, installed)
|
||
with `Ctrl+Shift+V` preview.
|
||
- App-menu launcher **"Mihomo VPN Dashboard"** → opens `http://127.0.0.1:9090/ui/`
|
||
(only works while the mihomo service is running).
|
||
|
||
---
|
||
|
||
## 10. Inventory — everything set up on the Kylin machine
|
||
|
||
| Area | What | State |
|
||
|------|------|-------|
|
||
| Access | SSH alias `kylin`/`kyln`, key login, passwordless sudo | ✅ active |
|
||
| Monitoring | SSH login logs (`auth.log`); command logging → `/var/log/cmdlog.log` | ✅ active |
|
||
| Monitoring | auditd execve rule | ⏳ queued (needs reboot + `audit=1`; may not take on Huawei firmware) |
|
||
| VPN | mihomo core + zashboard dashboard, subscription configured | ⛔ installed, **OFF** (stopped + disabled) |
|
||
| Browser | Chromium v149 (default), 360/CNOOC browser | ✅ installed |
|
||
| Editors | VS Code (`code`), pluma, WPS Office | ✅ pre-installed |
|
||
| IDE | Cursor 3.7.27 (`/opt/cursor`) via `env -i` wrapper | ✅ installed (see §11) |
|
||
| Desktop docs | `VPN_Guide.html` / `.md`, dashboard launcher | ✅ on desktop |
|
||
|
||
---
|
||
|
||
## 11. Cursor IDE (Electron) — install + crash fixes
|
||
|
||
Cursor (AI code editor, Electron-based) installed as an **extracted AppImage** at
|
||
`/opt/cursor` (currently **v3.7.27**, ARM64). It will NOT run with a normal launcher
|
||
on this Huawei/Kylin box — it needs a special wrapper.
|
||
|
||
### Why the special launcher (three Kylin/Huawei ARM64 problems)
|
||
1. **EFAULT machine-ID probe crash** — Cursor runs a command to generate a unique
|
||
machine ID; the Kylin kernel blocks that memory `write` as suspicious →
|
||
`Error: EFAULT: bad address in system call argument, write` → instant crash.
|
||
**Fix:** `env -i` strips the environment (especially D-Bus) so Cursor skips the probe.
|
||
2. **Mali GPU crash (段错误 / segfault)** — hardware rendering conflicts with Huawei's
|
||
Mali GPU driver (`dmesg`: `mali gpu: kctx ... create/destroyed` at crash time).
|
||
**Fix:** `--disable-gpu` (software rendering).
|
||
3. **Blocked sandbox** — `--no-sandbox`.
|
||
Also installed `xdg-desktop-portal` + `xdg-desktop-portal-gtk` (1.6.0) for the
|
||
Wayland file picker (necessary but not sufficient alone).
|
||
|
||
### The launcher (already installed)
|
||
- **Wrapper:** `/usr/local/bin/cursor-launch`
|
||
- **Start-menu entry:** `/usr/share/applications/cursor.desktop` → `Exec=/usr/local/bin/cursor-launch %F`
|
||
|
||
```sh
|
||
#!/bin/sh
|
||
exec env -i \
|
||
HOME="$HOME" DISPLAY="${DISPLAY:-:0}" PATH="$PATH" \
|
||
/opt/cursor/usr/share/cursor/cursor --no-sandbox --disable-gpu "$@"
|
||
```
|
||
|
||
### Updating Cursor to a newer version
|
||
1. Get the latest ARM64 AppImage URL (downloads on Windows through the VPN):
|
||
```bash
|
||
curl -sL -A "Mozilla/5.0" -H "Accept: application/json" \
|
||
"https://www.cursor.com/api/download?platform=linux-arm64&releaseTrack=stable"
|
||
# -> JSON with "downloadUrl" (…/Cursor-X.Y.Z-aarch64.AppImage) and "version"
|
||
curl -L -C - -o Cursor-new.AppImage "<downloadUrl>"
|
||
```
|
||
2. `scp` it to Kylin, then:
|
||
```bash
|
||
chmod +x Cursor-new.AppImage
|
||
cd /tmp && ./Cursor-new.AppImage --appimage-extract # -> /tmp/squashfs-root
|
||
sudo rm -rf /opt/cursor && sudo cp -r /tmp/squashfs-root /opt/cursor
|
||
sudo chown -R root:root /opt/cursor && rm -rf /tmp/squashfs-root
|
||
```
|
||
3. **No change needed** to the wrapper or menu entry — paths stay the same.
|
||
Verify glibc first (must need ≤ 2.31): `objdump -T /opt/cursor/usr/share/cursor/cursor | grep -oE 'GLIBC_[0-9.]+' | sort -V | tail`.
|
||
|
||
### Cleanup note
|
||
Old AppImages in `~/software/` (`Cursor-3.2.11`, `Cursor-3.7.27`) and the user's
|
||
manual extract `~/software/squashfs-root` (3.2.11) are redundant now that `/opt/cursor`
|
||
is the install — safe to delete to reclaim space.
|