# FR-193.A0 乾淨機手動跑 rootless：壞掉清單與實測（CM-3805）

**結論**：rootless Docker 本身一次就裝起來，十二項驗證主機層全過，重開機不登入 17 秒內容器自己起來。壞的全在 Agent 安裝包：`sudo ./install.sh` 原樣跑在第一步就停；讓它連到 guidant 的 Docker 後走到第六步，卡在 4 個 bind 掛載的擁有者沒有換算。其中設備指紋那一條**不會報錯**，只會靜默算出另一個指紋。設計 v1.2 有 5 處要在 A1～A6 開工前改，見最後一節。

- 實測日期：2026-11-10（VM 時間 UTC 06:20～07:50）
- 範圍：照決策者瘦身令只做四件事——①手冊第 2～4 節 ②以 guidant 身分起現行出貨包 ③十二項 ④traceroute 兩種模式。吞吐與 D2 的 B 選項在瘦身令之前已經量了，只放在附錄、不算驗收項。
- 心跳連不上主系統是預期行為，不列為壞掉項。

## 1. 環境

| 項目 | 值 |
|---|---|
| 主機 | `192.168.50.192`（`guidant-ai-e2e-2`），PVE VM，8 核／47 GB／98 GB |
| OS／核心 | Ubuntu 24.04.2 LTS，`6.8.0-149-generic` x86_64 |
| 前提 | `stat -fc %T /sys/fs/cgroup` → `cgroup2fs`；systemd 255；`/dev/tpmrm0` 存在 |
| 裝前 subuid | `jedi:100000:65536`（只有這一行） |
| 裝前 `ping_group_range` | `1 0`（**沒有人能用**，見 B1） |
| Docker | 官方 `docker-ce 5:29.9.3`、`containerd.io 2.4.2`、`docker-compose-plugin 5.7.0`、`docker-ce-rootless-extras 29.9.3`、rootlesskit 3.3.0、slirp4netns 1.2.1、runc 1.5.2 |
| Agent 包 | `guidant-agent-compose-1.7.4.tar.gz`（188 的 build 目錄，與 Agent `pyproject.toml` 版號一致），`sha256sum -c` OK |
| guidant 帳號 | uid 999、gid 987、子 uid／gid `165536:65536` |

## 2. 逐步紀錄

| 步 | 做了什麼 | 結果 | 花多久 |
|---|---|---|---|
| ① | 手冊第 2 節前提逐項查 | 全符合；但 `ping_group_range` 預設是 `1 0`（B1） | — |
| ② | 手冊第 3 節裝官方 Docker 一組，`systemctl disable --now docker.service docker.socket` | 裝好；**root 的 containerd 還在跑**（B2） | 112 秒 |
| ③ | `useradd --system --create-home --shell /usr/sbin/nologin guidant` | 建好，但**沒配子 uid**（B3）；照手冊範例補 `100000-165535` 會跟 jedi 重疊且不報錯（B4）；改用 165536 起 | — |
| ③ | `loginctl enable-linger guidant` | `Linger=yes` | — |
| ④ | 裝 `systemd-container`，`machinectl shell guidant@ /bin/bash -c '… dockerd-rootless-setuptool.sh install; systemctl --user enable docker'` | 一次成功，非互動可用 | 4 秒 |
| ⑤ | sysctl 全開、寫 `delegate.conf`、`daemon-reload` | 控制器清單立刻有五個，但**執行中的 dockerd 要重起才吃到** cpuset／io（B6） | — |
| ⑥ | `sudo ./install.sh`（原樣） | 第①步停：`docker 指令存在但連不上 daemon`（B8） | 秒級 |
| ⑥ | `sudo DOCKER_HOST=unix:///run/user/999/docker.sock ./install.sh`（不改檔，只指 socket） | 走完 ①～⑤；⑥ seaweedfs unhealthy，180 秒後退出碼 5（B10） | 687 秒（含 2×1.3 GB 載入） |
| ⑥ | 卡上指令 `sudo -u guidant DOCKER_HOST=… bash -c 'cd /srv/guidant-ai-agent && docker compose up -d'` | 變數全缺：`.env` 只在 `/run/guidant-agent`；明指 `--env-file` 則 `permission denied`（B9） | — |
| ⑥ | 4 處擁有者換算成子 uid、`/run/guidant-agent` 開給 guidant 讀，`compose up -d --force-recreate` | 三顆容器全 healthy | 37 秒 |
| ⑥ | 再跑一次 `install.sh`（升級路徑，帶 DOCKER_HOST） | 成功結束，但**把 4 處擁有者全改回 1000**；容器沒重建所以當下沒壞（B12） | 248 秒 |
| ⑦ | 重開機，不登入 guidant，跑十二項 | 主機層全過；手冊第 12 項指令對錯目標（見第 4 節） | 開機到兩顆 healthy 27 秒 |
| ⑧ | 容器內 traceroute 兩種模式 | UDP 通、ICMP 不通；ping 也不通（見第 5 節） | — |

## 3. 壞掉清單（訊息 → 原因 → 修法）

### 主機層（手冊與 A1）

| # | 看到什麼 | 原因 | 修法（已在 VM 驗過） | 影響卡 |
|---|---|---|---|---|
| B1 | `sysctl net.ipv4.ping_group_range` → `1	0` | Ubuntu 24.04 的核心預設就是「沒有人」；`/usr/lib/sysctl.d` 沒有任何檔設它。手冊寫「與 Ubuntu、RHEL 的預設一致」不對 | 照 D2 寫 `/etc/sysctl.d/90-guidant-rootless.conf`；手冊措辭改掉 | A1、A7、A8 |
| B2 | disable docker 之後 `ps` 仍有 `root containerd` | `containerd.service` 是獨立單元、預設 enabled，手冊只停 docker 兩支 | `systemctl disable --now containerd.service`；十二項第 3 項才會過 | A1、A7 |
| B3 | `grep guidant /etc/subuid` 沒有輸出 | Ubuntu 24.04 的 `useradd --system` 不配子 uid（手冊寫「多數發行版會自動配」） | `usermod --add-subuids/--add-subgids` 補 | A1 |
| B4 | 照手冊範例補 `100000-165535`，`usermod` 回 0，`/etc/subuid` 出現兩行 `100000:65536`（jedi 與 guidant） | `usermod` **不檢查區段重疊**。重疊＝兩個帳號的容器共用同一批主機 uid，隔離失效 | 起點＝`/etc/subuid`、`/etc/subgid` 所有區段 `start+count` 的最大值（本機 165536）；手冊範例要拿掉固定數字 | A1、A7 |
| B5 | `machinectl shell guidant@ /bin/false` 回 0 | `machinectl shell` 不回傳內層的結束碼 | A1 不能用它的退出碼判斷 setuptool 成敗，要事後 `docker info` 驗；另外 `sudo -u guidant XDG_RUNTIME_DIR=/run/user/999 systemctl --user is-active docker` 實測可用（linger 已開時），`systemd-container` 可能不必裝——setuptool 本身用 `sudo -u` 跑**未驗** | A1 |
| B6 | 寫完 `delegate.conf` 後 `--cpuset-cpus` 印 `Cpuset discarded`、`--device-write-bps` 印 `limit discarded` | 控制器已委派，但 dockerd 啟動時才讀；要重起 user manager | 設計 §3.1 第 6 步寫 `loginctl terminate-user`——實測之後 **linger 不會自己把 user manager 拉回來**（20 秒仍 inactive，socket 消失、容器全停）。要再 `systemctl start user@999.service`，約 30 秒 socket 才回應。之後 cpuset `0`、io.max `wbps=1048576` 生效 | A1 |
| B7 | `docker info` 的 Driver 是 `overlayfs`，不是 `overlay2` | Docker 29 預設 containerd snapshotter（`driver-type io.containerd.snapshotter.v1`） | 手冊與十二項第 5 項的判準改成「不是 vfs」 | A7、A9 |

setuptool 腳本另有一條：root Docker 的 `/var/run/docker.sock` 還在跑就中止（`Aborting because rootful Docker … is running`，要 `--force` 才過），所以 A1 要先停 root Docker 才呼叫 setuptool。這一條是讀腳本得知，本次照手冊順序先停了，沒有實際撞到。

### 安裝包（A3、A4、A5）

| # | 看到什麼 | 原因 | 修法（已在 VM 驗過） | 影響卡 |
|---|---|---|---|---|
| B8 | `✗ docker 指令存在但連不上 daemon——請確認 docker 服務已啟動（systemctl start docker）` | root 的 docker CLI 只看 `/var/run/docker.sock` | 帶 `DOCKER_HOST=unix:///run/user/<uid>/docker.sock` 就過。🔴 訊息叫客戶 `systemctl start docker`，照做會把 root Docker 打開，正好反方向；磁碟檢查也看錯位置（`/var`，實際映像在 `/home/guidant/.local/share/docker`，帶 DOCKER_HOST 後就對了） | A1、A3 |
| B9 | `sudo -u guidant … docker compose --env-file /run/guidant-agent/.env …` → `stat /run/guidant-agent/.env: permission denied` | `/run/guidant-agent` 是 root 700、`.env` 600 | 設計的做法（目錄 750 `root:guidant`、`.env` 640）可行；`host.key` 同目錄，維持 600 root 就讀不到。另一條路見 B11，不必開這個目錄 | A4 |
| B10 | seaweedfs 一直 `health: starting` → unhealthy；容器內 `cat /tls/seaweedfs.key` → `Permission denied` | install.sh:2333 `_storage_key_owner` 寫死 `chown 1000:1000`；主機 uid 1000 不在 guidant 的對映內，容器裡看成 65534 | `chown 166535:166535`（見 B13 的換算） | A4 |
| B11 | 以 `sudo -u guidant` 做 install.sh 會做的三件事都失敗；以 root CLI 指 guidant 的 socket 三件都成功（表在下方） | `sudo -u` 之後 CLI 讀寫檔的身分變成 guidant；root 專屬的暫存目錄、staging 目錄、`/run/guidant-agent` 全部進不去 | 見下表與第 7 節第 1 條 | A3、A4、A5 |
| B12 | 再跑一次 install.sh：4 處擁有者全回到 `1000` | `_storage_key_owner`、`prepare_agent_host_paths` 每次都重設 | 4 處全部改走換算函式，重跑才不會蓋回去 | A4 |
| B13 | 主機上 `ps` 看到 Agent 主程式 uid 是 **166535**，不是手冊與設計寫的 166536 | rootless 的對映是 `/proc/<dockerd>/uid_map` = `0 999 1` + `1 165536 65536`：容器 root＝guidant 本人，容器 uid N（N≥1）＝起點 + N − 1 | 設計 §3.3 `host_uid_for` 要寫成 `BASE + N - 1`（N≥1），N=0 回 guidant 自己的 uid | A4 |
| B14 | Agent healthy，log 印 `device_uuid computed: c9fdd2cb…`；讀得到時是 `e8f2c8cb…` | `prepare_agent_host_paths` 把 `host-id/product_uuid` 設成 `0:1000 0440`，容器 uid 1000 讀不到，指紋只用 machine-id 算。**沒有任何錯誤**，只會讓主系統多出一台陌生 Agent | 擁有者 `999:166535`、0440（擁有者＝guidant＝容器內 uid 0，群組＝容器內 uid 1000）。這樣服務與 `fingerprint` 子命令（`exec -u 0`）都讀得到，兩邊都算出 `e8f2c8cb…`。只設 `0:166535` 的話服務對了，`fingerprint` 子命令仍讀不到，算出 `c9fdd2cb…` 並警告兩邊不一致 | A4 |
| B15 | 容器寫 `/data/content/cache` 失敗；讀 `/factory/factory-defaults.env` 失敗 | 同一支 `prepare_agent_host_paths` 的另兩處 `AGENT_UID`；**設計 §2.2 盤點漏了這支函式**（只列 install.sh:2333、upgrade_order.sh:91、local_admin.sh:28） | `content/cache` → `166535:166535`；`factory-defaults.env` → `0:166535` 0640 | A4 |
| B16 | 容器寫 `/upgrade/agent-order` → `Permission denied` | `upgrade/agent-order` 是 `1000:1000 750` | `chown 166535:166535` 後可寫 | A4、A5 |
| B17 | `guidant-agent-upgrade-order.service`、`guidant-agent-upgrade-boot-check.service` 失敗：`已安裝的 agent 映像不含此子命令（映像內找不到 install-lib）` | 單元以 root 跑、不帶 DOCKER_HOST，看不到 guidant 的映像 | 單元內 docker 一律走同一個入口（A3 定的那支） | A5 |
| B18 | 07:15:14 root 的 `dockerd` 與 `containerd` 被拉起來 | `guidant-agent-upgrade-order.service` 有 `Wants=docker.service`，install.sh 建好 `agent-order` 時 path 單元觸發 | `Wants`／`After` 改成 `user@<uid>.service`（設計 §3.4 只寫改 `After`，`Wants` 也要改）；本次手動停回 | A5 |
| B19 | `sudo guidant-agent-compose status` 顯示兩顆都 `未建立` | 維運入口同樣不帶 DOCKER_HOST | 隨 A3 的共用入口一起好 | A3 |

**B11 的對照表**（同一台、同一個 socket，只差 CLI 用誰的身分）：

| 動作（install.sh 裡實際會做的） | `sudo -u guidant`（設計 §3.2 的 `dk`） | root CLI ＋ `DOCKER_HOST` |
|---|---|---|
| `image_read_file`：`docker cp` 到 root 的 `mktemp -d`（700），之後 `source` | `permission denied`，取不到 install-lib | 成功 |
| `docker load -i` 讀 root 700 目錄裡的 tar（升級 staging） | `permission denied`（改成 stdin 餵可以） | 成功 |
| `docker compose --env-file /run/guidant-agent/.env`（現行 700／600） | `permission denied` | 成功 |
| 容器 bind 掛主機 `/etc/shadow`（640 root:shadow）後讀 | `Permission denied` | `Permission denied`（daemon 一樣是 guidant，root CLI 沒有多給容器權限） |

## 4. 十二項驗證（重開機後、未登入 guidant）

| # | 檢查 | 指令 | 實際輸出 | 結果 |
|---|---|---|---|---|
| 1 | 系統層 Docker 沒在跑 | `systemctl is-active docker`（另查 docker.socket、containerd） | `inactive` ／ `inactive inactive` | ✔ |
| 2 | guidant 的 Docker 開機自起 | `sudo machinectl shell guidant@ /bin/systemctl --user is-active docker` | `active` | ✔ |
| 3 | 主機無 root dockerd | `ps -eo user,comm \| grep -E 'dockerd\|containerd'` | `guidant containerd`／`guidant containerd-shim` ×2／`guidant dockerd` | ✔（要先做 B2，否則有一行 `root containerd`） |
| 4 | rootless 模式 | `machinectl shell guidant@ /usr/bin/docker info --format '{{.SecurityOptions}}'` | `[name=seccomp,profile=builtin name=rootless name=cgroupns]` | ✔ |
| 5 | 儲存驅動 | `docker info --format '{{.Driver}} / {{.DriverStatus}}'` | `overlayfs / [[driver-type io.containerd.snapshotter.v1]]` | ✔（判準改「不是 vfs」，B7） |
| 6 | cgroup 委派 | `{{.CgroupDriver}} {{.CgroupVersion}}`；`user@999.service/cgroup.controllers` | `systemd 2`；`cpuset cpu io memory pids` | ✔ |
| 7 | 子 uid 對映 | `grep guidant /etc/subuid /etc/subgid` | `guidant:165536:65536` 各一行 | ✔ |
| 8 | linger | `loginctl show-user guidant -p Linger` | `Linger=yes` | ✔ |
| 9 | ping sysctl | `sysctl net.ipv4.ping_group_range` | `0	2147483647` | ✔ |
| 10 | 容器內 ping | 手冊指令（alpine，目標換 188） | `1 packets transmitted, 1 packets received` | ✔（alpine 以容器 root 跑）。**Agent 容器不通**，見第 5 節 |
| 11 | 資源上限 | `docker run --rm --memory 64m alpine cat /sys/fs/cgroup/memory.max` | `67108864`；Agent 實際 `memory.max=4294967296 pids.max=512`、seaweedfs `2147483648`／`1024` | ✔ |
| 12 | 資料目錄 owner | `stat -c '%U %u' /srv/guidant-ai-agent/upload` | `No such file or directory` | ✘ 指令對錯目標：資料在 named volume。改查 `/home/guidant/.local/share/docker/volumes/guidant-ai-agent_agent-upload/_data` → `166535:166535`；bind 掛載 `content/cache`、`seaweedfs.key`、`upgrade/agent-order` 也是 `166535`（手動修過，B10～B16） |

另記：重開機後 `/run/guidant-agent` 由 secrets 單元重建為 root 700，`.env` 600——B9 的權限要寫在 `secrets.sh load` 裡（設計 §3.3 已這樣寫），不能只在安裝時做一次。開機時間線：uptime 17 秒 agent healthy、27 秒 seaweedfs healthy；`loginctl list-users` 只有 `guidant … lingering` 與 jedi。

## 5. 容器內 traceroute 與 ping（④）

目標 `192.168.50.188`。Agent 容器照現行 compose：uid 1000、`cap_drop: [ALL]`、`cap_add: [NET_RAW]`、`no-new-privileges`。參數照 `netdiag/adapters/probes/traceroute.py`、`ping.py` 的 `build_argv`。

| 指令 | rootless（現行 compose） | root Docker 對照（同一顆映像、同一組參數） |
|---|---|---|
| `traceroute -n`（UDP） | ✔ 通：`1 172.18.0.1` → `2 10.0.2.2` → `3 192.168.50.188` | ✔ 通 |
| `traceroute -I -n`（ICMP） | ✘ `You do not have enough privileges to use this traceroute method. socket: Operation not permitted` | ✔ 通 |
| `ping -n -c2 -W2 -w10` | ✘ `ping: socket: Operation not permitted`（走 SOCK_RAW） | ✔ 通（走 SOCK_DGRAM） |
| `ping -M do -s 1472`（mtu） | ✘ 同上 | ✔ 通 |

**原因**：容器內看到的 `ping_group_range`，root Docker 下是 `0 2147483647`，rootless 下是 `65534 65534`。rootless 的容器網路 namespace 不帶主機的值，uid 1000 拿不到免特權的 ICMP socket，只好走 raw socket，而 `no-new-privileges` 下 NET_RAW 不會生效。

**修法（已驗）**：compose 的 agent 服務加 `sysctls: { net.ipv4.ping_group_range: "1000 1000" }`，`cap_add: [NET_RAW]` **保留**：

| 組合 | ping | mtu 1472 | traceroute | traceroute -I |
|---|---|---|---|---|
| 現行（NET_RAW，無容器 sysctl），主機全開 | ✘ | ✘ | ✔ | ✘ |
| NET_RAW ＋ 容器 sysctl `1000 1000`，主機全開 | ✔ | ✔（1473 回 `message too long, mtu=1500`，判斷正確） | ✔ | ✔ |
| 同上，主機只開 guidant 自己的 gid（`987 987`） | ✔ | ✔ | ✔ | ✔ |
| 同上，主機維持 Ubuntu 預設 `1 0` | ✘（`+1 errors`） | ✘ | ✔ | 三次裡一次失敗、兩次通 |
| **拿掉** NET_RAW ＋ 容器 sysctl | ✘ `/bin/sh: ping: Operation not permitted` | ✘ | ✔ | ✔ |

最後一列推翻設計 A6「compose 拿掉 NET_RAW」。映像裡的 `/usr/bin/ping` 帶檔案能力 `cap_net_raw=ep`，能力不在 bounding set 時連 exec 都失敗。root Docker 下拿掉 NET_RAW 結果一樣，跟 rootless 無關。

有一處沒解釋清楚：`docker exec guidant-ai-agent ping …` 直接執行時會通（走 SOCK_RAW），經 `sh -c` 或從容器內程式 fork 出來就不通。兩者讀 `/proc/self/status` 的 CapEff 都是 0。netdiag 是從 Agent 主程式（PID 1，CapEff 0、NoNewPrivs 1）fork 出 ping，比較接近 `sh -c` 那條。所以驗收要以 fork 出來的結果為準，不能拿 `docker exec ping` 當證據。本卡沒有接主系統，沒有從主控台真的觸發 netdiag，留給 A6／A9 驗。

## 6. 資安規範對照

| 規則（Agent `.claude/rules/security/`） | 本次對到的事 |
|---|---|
| secrets：帳密只在記憶體解開（SR-06.4） | `/run/guidant-agent/.env` 要給 Docker CLI 讀。走 root CLI（B11）就不必開權限；若照設計開 750 `root:guidant`，同目錄的 `host.key` 必須維持 600 root。容器的環境變數（含 `SEAWEEDFS_SECRET_KEY`）落在 `/home/guidant/.local/share/docker/containers/<id>/config.v2.json`（guidant 600），能以 guidant 身分讀檔的人讀得到，跟 root Docker 下「root 讀得到」同級，資安報告 SR-09.5 要寫清楚 |
| sync-and-identity：註冊憑證名稱等於本機指紋 | B14：指紋來源讀不到時靜默變成另一個值。A4 驗收要比對「服務 log 的 `device_uuid`」與「`fingerprint` 子命令」兩邊一致 |
| external-calls-and-tools：外部程式參數陣列、核定的網路除錯十項 | 第 5 節：只改 compose 的 sysctl，不改 netdiag 參數；`traceroute -I` 可以保留 |
| 升級單與包一律不可信（`upgrade_order.sh` 檔頭） | rootless 之後映像庫屬 guidant（`/home/guidant/.local/share/docker`，710／700）。install.sh 以 root `source <(image_read_file …)` 載入 install-lib，等於 root 執行 guidant 可改的東西：拿到 guidant 身分的人可以換映像、進而以 root 執行。root Docker 時代沒有這條路。A3／A5 要決定怎麼補（例如只從已驗章的主機副本 source） |

## 7. 對後面九張卡的建議修正

1. **A3（要決策者拍板）**
   - 問題：設計 §3.2 的 `dk()` 用 `sudo -u guidant`，install.sh 的三個基本動作會壞（B11）：從映像取 install-lib、升級 staging 的 `docker load`、`--env-file`。
   - 建議：`dk()` 改成 root CLI ＋ `DOCKER_HOST=unix:///run/user/$RUN_UID/docker.sock`。
   - 理由：本次 install.sh 原樣帶這個變數就跑完 ①～⑤；daemon 仍是 guidant、容器能力沒有變多（`/etc/shadow` 對照）。A4 也就不必把 `/run/guidant-agent` 開給 guidant 讀。維運入口（B19）與兩支升級單元（B17）一起好。
2. **A4**：`host_uid_for` 寫成 `BASE + N - 1`（N≥1），N=0 回 guidant 的 uid（B13）。要換算的地方從 3 處擴成 6 處：`_storage_key_owner`、`prepare_agent_host_paths` 的 product_uuid／content/cache／factory-defaults.env、`upgrade/agent-order`、`local_admin.sh:28`。product_uuid 用 `guidant:BASE+999 0440`（B14）。重跑 install.sh 不能把擁有者蓋回 1000（B12）。
3. **A1**：停 root Docker 時連 `containerd.service` 一起停（B2）；子 uid 起點自己算、不能沿用 usermod 的預設，也不能用固定數字（B3、B4）；委派後要 `systemctl start user@<uid>.service` 並等 socket（最多約 30 秒），不能只 `terminate-user`（B6）；不能用 `machinectl shell` 的退出碼判斷成敗（B5）；`check_environment` 的「請 systemctl start docker」訊息要改（B8）。
4. **A5**：三支單元的 `Wants=docker.service` 也要換成 `user@<uid>.service`，不然會把 root Docker 拉起來（B18）。
5. **A6**：**不要拿掉 `cap_add: [NET_RAW]`**；改成加 `sysctls: { net.ipv4.ping_group_range: "1000 1000" }`。`traceroute -I` 不用改成 UDP（第 5 節）。uninstall 要記得 containerd 在 B2 被停過。
6. **D2**：主機的值要涵蓋 guidant 的主 gid，子 gid 區段沒用（附錄）。全開照樣成立；「只開 guidant 的 gid」也實測可行，要不要收緊留給決策者。
7. **A7（手冊）**：Ubuntu 的 ping 預設（B1）、`useradd` 不配子 uid（B3）、範例區段會重疊（B4）、containerd（B2）、`overlayfs`（B7）、「起點＋1000」改成「起點＋999」（B13），第 12 項改查 volume 路徑。
8. **A9**：十二項第 10 項改用 Agent 容器、從容器內程式 fork 出 ping 來驗（第 5 節），並加一項「服務指紋＝`fingerprint` 子命令」。

## 8. VM 現況（留給後面的卡）

- rootless 堆疊在跑，三顆容器 healthy；B10～B16 的擁有者是**手動修好的**（product_uuid 為 `999:166535 0440`），重跑 install.sh 會被蓋回。
- root Docker 三支單元 disabled＋inactive；`ping_group_range` 全開；`delegate.conf` 已寫。
- 量測殘留：`/tmp/100m`、`~/a0/`（交付包與 `sink/` 收端腳本）、`iperf3` 套件、guidant 映像庫裡的 `alpine:3.20`、`curlimages/curl:8.11.1`、`networkstatic/iperf3`。這台本來就給本案拆裝用，沒清。

## 附錄：瘦身令前已量到的數字（不在驗收範圍）

- **上傳 100 MB HTTPS**（收端是同一台 VM 上的 TLS server，測的是容器網路驅動本身的上限）：主機直連 0.41～0.45 秒；root Docker 0.49～0.53 秒（約 200 MB/s）；rootless（slirp4netns）1.78～2.11 秒（約 50～58 MB/s）。iperf3 出站 419～466 Mbit/s。rootless 慢約 4 倍，但每秒仍有 50 MB 以上，D3（用 slirp4netns）成立。對 188:443 量不到：nginx 回 413／405，不收 body。
- **D2 B 選項**（只開 guidant 的子 gid 區段 `165536 231071`）：alpine 以容器 root ping **不通**；只開 guidant 主 gid `987 987` 就通。真正發 ICMP 的是以 guidant 主 gid 執行的 slirp4netns，子 gid 區段對它沒有作用。
