# 自更新

> HTML: https://remote.ivjn.us/docs/zh-TW/self-update/

Windows x64/arm64、Linux x64/arm64（glibc 與靜態 musl 建置）、macOS Apple Silicon 以及 FreeBSD/NetBSD x64/arm64 上的 CLI 與獨立 Agent 可以從發布網站更新自身的執行檔：

```sh
remote-cli update self --check
remote-cli update self
remote-agent update self --check
remote-agent update self
```

| 平台 | 發布套件 | 一次性 `remote-agent update self` 後的重新啟動方式 |
| --- | --- | --- |
| Windows x64 / arm64 | `remote-windows-x64.zip` / `remote-windows-arm64.zip` | 排程工作 `RemoteAgent`，其次重新啟動處理程序 |
| Linux glibc x64 / arm64 | `remote-linux-x64.tar.gz` / `remote-linux-arm64.tar.gz` | systemd 系統或使用者單元、OpenRC，其次重新啟動處理程序 |
| Linux musl x64 / arm64 | `remote-linux-musl-x64.tar.gz` / `remote-linux-musl-arm64.tar.gz` | 與 Linux glibc 相同 |
| macOS Apple Silicon | `remote-macos-arm64.tar.gz` | LaunchAgent `us.ivjn.remote-agent` |
| FreeBSD / NetBSD x64 / arm64 | `remote-freebsd-*.tar.gz` / `remote-netbsd-*.tar.gz` | `remote-agent install --system` 註冊的 rc.d `remote_agent`；否則需手動重新啟動 |

更新器會保持目前執行檔的 C 執行階段：musl 建置只安裝 musl 套件，glibc 建置只安裝 glibc 套件。OpenWrt 即使是 x64/arm64 也繼續使用 opkg/apk：其套件不含封存更新器，在存在 `/etc/openwrt_release` 的主機上執行的 musl 套件也會拒絕自我更新。只有 manifest 列出對應發布套件的平台才會更新；在支援該平台之前建置的安裝需手動替換一次。

無需登入帳號或設定裝置。

首次安裝 Agent 本體（二進位檔案、私有設定和系統自動啟動）時，優先使用 `remote-agent install` / `remote-agent uninstall` ，適用於 Linux、macOS 和 Windows，參見 [platforms.md](/docs/zh-TW/platform-installation.md)。 該命令不需要網路訪問；配對仍需單獨完成。包含 `install` 的二進位檔案在機器上安裝一次後，後續升級可以使用 `update self` ，或在支援的平臺上使用自動更新。

`--check` 優先讀取清單中的執行檔 SHA-256，無需下載完整封存檔；清單缺少執行檔中繼資料時才下載驗證。雜湊比較能區分版本號相同的開發建置，檢查不會修改安裝。安裝前拒絕降級，並核對執行檔版本、大小和雜湊。

預設來源是 `https://r2.ivjn.us` （託管 `downloads/`的 R2 公共域名）。 `--source https://your-mirror.example/releases` 用於選擇可信映象，映象需提供相同的 `downloads/manifest.json` 和歸檔佈局。GitHub Releases ([mbcc2006/remote-control releases](/#downloads)) 作為**額外的**下載渠道釋出同類桌面歸檔包；它們**不會**替代網站佈局，自更新仍使用 `r2.ivjn.us` ，除非將 `--source` 指向提供 `downloads/manifest.json` 的映象。僅接受 HTTPS，拒絕重新導向，不傳送帳號令牌。系統根據當前作業系統和架構選擇歸檔名稱，按照 HTTPS 清單校驗其大小和 SHA-256，並僅提取準確匹配的可執行檔案條目。桌面用戶端先用內建 Ed25519 公鑰驗證 `downloads/manifest.json.sig`，再信任清單。缺少簽名、內容遭竄改或未知金鑰都會拒絕更新。鏡像必須提供原樣的已簽名清單與簽名檔；啟用強制驗證的用戶端前先發佈簽名檔。

安裝前，新可執行檔案必須透過有時間限制的 `--version` 檢查。更新透過同目錄中的 `.update.lock` 檔案序列執行，並重新檢查當前可執行檔案以檢測併發替換。更新只修改可執行檔案及其更新/備份檔案；裝置 ID、憑證和設定檔案均會保留。

在 macOS 上，下載的可執行檔案還必須先透過 `codesign --verify --strict` ，再檢查版本。替換時保留髮布時的簽名和位元組，使後續雜湊比較保持穩定。

舊可執行檔案保留為 `<executable>.previous`。 必要時，停止 Agent 並將該檔案恢復到原可執行檔案路徑。在 Windows 上，安裝替換檔案前會將正在載入的可執行檔案重新命名為備份；安裝失敗時會恢復原名稱。

成功執行 `remote-agent update self`（非 `--check`）後，更新器會嘗試重新啟動正在執行的 Agent 以載入新程式：Linux（glibc 與 musl）上依序執行 `systemctl restart remote-agent`、`systemctl --user restart remote-agent`，存在 `/etc/init.d/remote-agent` 時再執行 `rc-service remote-agent restart`（OpenRC），都無法使用時以原參數 SIGTERM 重新啟動其他 `remote-agent` 常駐程式；Windows（x64 與 arm64）上重新啟動排程工作 `RemoteAgent`，並有處理程序層級的備援；FreeBSD/NetBSD 上執行 `remote-agent install --system` 安裝的 rc.d 指令碼 `remote_agent restart`，沒有時提示需手動重新啟動。若執行更新的處理程序本身就是常駐程式（參數不是 `update self`），Unix 上會 `exec` 新程式（保留 PID 供服務管理員使用），Windows 上會啟動一個分離的替代處理程序。`remote-cli update self` 不會重新啟動 Agent。狀態列會說明重新啟動使用了 systemd、OpenRC、rc.d、排程工作、新啟動的處理程序，或未能重新啟動（更新本身仍然成功）。請以安裝擁有者的權限執行；更新器不會提升權限。重新啟動時記錄的命令列會隱藏 `--token` 的值。如果舊的 Windows Agent 仍在從備份檔執行，請先重新啟動它再更新。

## 自動更新（桌面 Agent 守護程序）

當 Agent 守護程序執行時（非單次 `update self`），它會**定期檢查**相同的網站清單，並在釋出的 `remote-agent` 二進位檔案不同時應用更新（透過雜湊比較）。預設行為：

- 在支援的桌面建置上**預設啟用**（Windows x64/arm64 / Linux x64/arm64 glibc 與 musl / macOS arm64 / FreeBSD 與 NetBSD x64/arm64）。
- 可透過環境變數 `REMOTE_AUTO_UPDATE=0` （也接受 `false` / `no` / `off`）、JSON 設定 `"auto_update": false`，或 CLI 引數 `--auto-update false` 禁用。
- 啟動約 90 秒後首次檢查；之後約每 12 小時檢查一次（並非每次心跳都檢查）。
- 清楚記錄進度；**絕不**向更新 CDN 傳送裝置令牌。
- OpenWrt（包括在 OpenWrt 主機上執行的 musl 套件）會跳過桌面自動更新；Android 使用自己的 OTA 路徑。Intel Mac、32 位元 ARM 等未對應的架構會返回無發布套件錯誤，不能自動升級。

## 遠端下發更新（操作員 → 裝置）

擁有普通裝置會話的操作員（ACL 與 `OpenExec`相同）可以要求線上裝置檢查或應用更新：

協議（WebSocket JSON）：

- `OperatorUp::update_device` `{ session_id, device_id, check_only, source? }`
- Exchange → `DeviceDown::update_req`
- 裝置 → 持續返回 `DeviceUp::update_status` / `OperatorDown::update_status` ，包含 `phase`：
  `checking` | `downloading` | `applying` | `up_to_date` | `available` | `applied` | `error`

控制台：在裝置詳情中選擇 **檢查更新** / **下發更新**。

CLI（與其他裝置會話操作方式相同）：

```sh
remote-cli device update <device_id> --check
remote-cli device update <device_id>
remote-cli device update <device_id> --source https://r2.ivjn.us
```

啟用 LAN 時，區域網控制對 `update_device` 的對映方式與其他 `OperatorUp` open-* 訊息相同。

OpenWrt 和其他不支援的 Agent 會返回明確的 `error` 階段，而不會安裝錯誤的產物。Windows arm64、Linux musl、macOS Apple Silicon 與 FreeBSD/NetBSD 使用與 Windows x64、Linux glibc 相同的桌面封存路徑。

## Android Agent OTA

Android Agent APK（`remote-agent-android.apk` 位於網站上）可以透過同一 HTTPS 清單更新自身：

在 Agent 中開啟 **Settings / 設定 → 檢查更新** 手動檢查。檢查會下載並驗證 APK；發現更新後，還需單獨確認才會開啟 Android 安裝器。

- 下載 APK，驗證**位元組大小與 sha256**，將 `versionCode` 和包名與已安裝應用比較（版本相同但雜湊不同的重新建置仍可能安裝）。
- 透過 `PackageInstaller` 安裝。應用宣告瞭 `REQUEST_INSTALL_PACKAGES`；首次 OTA 時，使用者可能需要在系統設定中允許 Remote Agent“安裝未知應用”，並在系統安裝器介面出現時確認。
- 開始安裝會返回 `available` 和等待安裝的訊息，而非 `applied`。
  私有安裝器回撥會開啟系統確認介面。對同一個待安裝 APK 的重複請求會複用會話；失敗和過期的自更新會話會被放棄。Agent 重新連線後，請確認已安裝的版本和雜湊。
- 當 `AgentService` 執行時，自動檢查會以類似間隔執行。
- 遠端 `update_req` 由原生 Agent 處理，並呼叫 APK 控制橋 （`action: ota_update`）。
- 替換安裝包時會保留應用私有儲存（裝置 ID / 令牌）； `MY_PACKAGE_REPLACED` 在啟用時仍會重啟連線。

所有元件統一使用發布版本 0.1.27：Rust Agent/CLI/Exchange、瀏覽器擴充功能、Android 應用程式和 Web 控制台。後續一起遞增版本，Android versionCode 為 30。打包前執行 `python scripts/check-release-versions.py`。

## Android 控制台更新

原生控制台有獨立的**設定 → 檢查更新**按鈕，登入前後均可使用。它不攜帶帳號憑證讀取公開網站清單，並將已安裝建置與釋出的 `versionCode` 比較。如果清單不包含建置後設資料且對外展示的版本一致，它會提供下載，而不會聲稱已安裝應用是最新版本。

**下載安裝包**會在瀏覽器中開啟網站 APK；請透過 Android 的確認介面完成安裝。該操作更新當前手機上的控制台。裝置詳情頁的更新控制元件仍針對相應的遠端裝置。

## 控制台“檢查更新”（合併流程）

裝置詳情只保留一個“檢查更新”操作：先發 `update_device { check_only: true }`；
- `up_to_date` → 顯示“已是最新版本 x.y.z”；
- `available` → 顯示“當前 → 最新（大小）”，使用者選擇“下發更新 / 稍後”，確認後發 `check_only: false` 並顯示進度與結果；
- Android 下發後返回 “Installation pending …” 時提示在裝置上確認系統安裝；
- 裝置離線、瀏覽器裝置直接給出原因；平臺不支援時顯示“此裝置不支援遠端更新”。
結果訊息附帶 `current <版本>` 和 `size <字节> bytes`。

另請參閱倉庫中的 `docs/watchdog.md` ，瞭解可選的存活監視程式 （`--watchdog`、`REMOTE_WATCHDOG=1`）。

Android 診斷頁獨立顯示最近一次自動更新結果與時間，不依賴 READ_LOGS 權限。固定原因區分 HTTP 狀態、DNS、逾時、TLS、清單、APK 大小/雜湊及安裝權限失敗。同一安全訊息以 RemoteAgentOTA 標籤寫入 logcat；完整裝置日誌仍要求 READ_LOGS。狀態不會複製例外中的 URL、路徑或憑證。
