#!/usr/bin/env bash # leo21c-write-guard.sh — 不准寫 leo 的個人帳號(leo 2026-08-20 立) # # leo 原話: # 「youlin = stage,geek6688 = 測試 prod & 出貨機,uncle6 = 中心服務, # 這三個都可以拿來當 default,但你要實驗當然是放在 youlin。」 # 「**leo21c 就是我這個普通用戶,不應該讓你去操控,我只用公開的更新。**」 # # 🔴 為什麼要機器守(2026-08-20 實錯,本閘的來由): # 總管派 subagent 驗碎形目錄索引,它用了本機 `~/.arcrun/config.yaml` 的預設 # (`cypher_executor_url: …leo21c…`/`api_key: bfezv28v`), # 把 8 張測試卡寫進 leo 的真庫。 # 而當時 `system-dev/wiki/agent-memory.md` §2 白紙黑字寫著 # 「底層帳號=leo21c,**任何自動化只准落在它上面**」——那是 dogfood 時代的舊分工。 # ⇒ **文件教錯 + 機器預設也錯 ⇒ 沒指定的一律流進他的帳號。** # ⇒ 規則改對了還不夠:`~/.arcrun/config.yaml` 至今仍指著 leo21c # (它的 KV id 與 encryption_key 是該實例專屬,換不過去), # 所以**真正擋得住的是這道閘**,不是那張表。 # # 判準(封動作,不封措辭 —— 同 empty-handed-stop-guard 的哲學): # 命中 leo21c 的座標 + 這是一個寫入動作 ⇒ 擋 # 只是讀(GET/查詢/grep 到那個字串) ⇒ 放行 # 發一則 notify_leo(通知型 trigger,什麼都沒改)⇒ 放行(同 prod-write-guard 的白名單) set -uo pipefail payload=$(cat) cmd=$(printf '%s' "$payload" | python3 -c " import json,sys try: print((json.load(sys.stdin).get('tool_input') or {}).get('command','')) except Exception: print('') " 2>/dev/null) [ -z "$cmd" ] && exit 0 # 🔴 這些工具碰不到 CF 帳號 ⇒ 整個放行(2026-08-20 上線當天就誤攔兩次,本段是修正) # ① `git commit -m "…leo21c… acr update…"` 同時命中座標與寫入動詞而被擋 # ② 連「修這道閘本身」的指令都被擋(測試案例裡自然含觸發字) # 但 git 與文字編輯根本寫不到那台實例,**訊息裡寫到什麼都不會造成寫入**。 # leo 2026-08-17:「紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎。」 # ⇒ 閘要問「這個指令能不能真的寫到那台」,不是「這段文字提到什麼」。 case "$cmd" in git\ *|jj\ *|" git "*) exit 0 ;; esac if printf '%s' "$cmd" | grep -qE '^[[:space:]]*(git|jj)[[:space:]]'; then exit 0 fi # 修這道閘自己:命中的是本檔路徑就放行(否則永遠改不動它) if printf '%s' "$cmd" | grep -q 'leo21c-write-guard'; then exit 0 fi # ── 判準:這個指令會不會真的把東西寫到那台?──────────────────────────────── # 🔴 2026-08-20 第三次誤攔後收斂(前兩次:git commit 訊息、修閘自己)。 # 舊判準是「文字裡有沒有出現座標」+「有沒有寫入動詞」,於是 # `curl -X PATCH .../api/v1/repos/inkstone/Arcrun/issues/145 -d '{"state":"closed"}'` # **只因為票的內文提到那個帳號就被擋**——而那是打 Gitea,跟 CF 一點關係都沒有。 # ⇒ 只認**兩種真的會寫到那台的形狀**,其餘一律不管文字寫了什麼: # ① 寫入請求打到 `*.leo21c.workers.dev` # ② 用它的 CF 帳號 id 去跑 wrangler/acr # leo 2026-08-17:「紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎。」 hit=0 # ① 寫入請求打到那台 worker if printf '%s' "$cmd" | grep -qiE 'https?://[^[:space:]"'"'"']*leo21c\.workers\.dev'; then # 🔴 (a) 通知型 trigger 放行(inkstone/ISEP#130;判準與 prod-write-guard.sh 同一份): # `…/webhooks/named//notify_leo/trigger` = 發一則 Telegram 給 leo,**什麼都沒改**。 # wiki `agent-memory.md` 寫明它是找 leo 的正式通道;prod-write-guard 在 ISEP#63 就放行了它, # 本閘卻因為路徑尾巴是 `/trigger` 照擋 ⇒ 兩支閘對同一條指令說不同的話, # 而「發通知」被擋的結果是**雲端撞到人閘也叫不動 leo**(inkstone/InkStoneCo#110)。 # 放行的範圍刻意只有一個名字:指令裡打到 leo21c 的**每一個**網址都得是它, # 混進部署端點(`…/webhooks/named` 無名字)或別的工作流 ⇒ 照擋。 NOTIFY_ONLY=$(printf '%s' "$cmd" | python3 -c ' import re, sys cmd = sys.stdin.read() ALLOW = {"notify_leo"} urls = [u for u in re.findall(r"https?://[^\s\"\x27<>]+", cmd) if "leo21c.workers.dev" in u.lower()] PAT = re.compile(r"https?://[^/]+/webhooks/named/[^/]+/([A-Za-z0-9_-]+)/trigger/?$") def notify(u): m = PAT.match(u.rstrip(",;)")) return bool(m) and m.group(1) in ALLOW print("notify-only" if urls and all(notify(u) for u in urls) else "") ' 2>/dev/null || printf '') if [ "$NOTIFY_ONLY" != "notify-only" ]; then # 🔴 (b) 先剪掉唯讀工具的 `-d`(inkstone/ISEP#130,09-04 雲端實撞): # `curl -s https://…leo21c…/x | tr -d '\r'`、`cut -d= -f2`、`sort -d`、`date -d` …… # 這些 `-d` 沒有一個會寫到那台,舊判準 `-d[[:space:]]` 卻把整條純 GET 擋下 # ⇒ 「連讀都被擋」。prod-write-guard.sh 在 2026-08-12 就修過同一個洞,本閘漏了。 # 剪法與那支同一句 sed,剪完剩下的 `-d` 才是 curl 的 body。 cmd_w=$(printf '%s' "$cmd" | sed -E 's/(^|[|;&( ])(tr|cut|sort|uniq|date|xargs|paste|join|du|logger|split|comm)[[:space:]]+-d/\1\2 __READONLY_D__/g') if printf '%s' "$cmd_w" | grep -qE -- '-X *(POST|PUT|PATCH|DELETE)|--request *(POST|PUT|PATCH|DELETE)|--data|--data-raw|-d[[:space:]]|/trigger'; then hit=1 fi fi fi # ② 拿它的 CF 帳號去部署 if printf '%s' "$cmd" | grep -qE 'CLOUDFLARE_ACCOUNT_ID=51a01bfa2665bd7bc3fd080dc40cf3e1'; then if printf '%s' "$cmd" | grep -qE 'wrangler|acr |node .*index\.js'; then hit=1 fi fi [ "$hit" = "1" ] || exit 0 cat >&2 <<'MSG' 🚫 不准寫 leo 的個人帳號 leo21c(leo 2026-08-20 立) leo 原話:「**leo21c 就是我這個普通用戶,不應該讓你去操控,我只用公開的更新。**」 youlin ← 🟢 你的 stage:做實驗、跑驗證,**沒指定就用這個** geek6688 ← 測試 prod + 出貨機 uncle6 ← 中心服務(安裝器/文件站/bundle),出貨線的目的地 leo21c ← 🔴 leo 本人在用的知識庫。**讀可以,寫不行。** 改法:把目標明寫成 youlin,不要吃 `~/.arcrun/config.yaml` 的預設—— 那個檔至今仍指著 leo21c(KV id 與 encryption_key 是該實例專屬,換不過去)。 cypher : https://arcrun-cypher-executor.arcrun-yuga3bse.workers.dev (09-02 重裝後的子網域;舊名 youlin-hsieh-dev 的 DNS 已不存在,打它一律 000) ns : yuga3bse CF : 1129efd7df2e8899d537e9c8fbabb6cb token : 頂層 .env 的 CLOUDFLARE_API_TOKEN_YOULIN_CC_USE 📌 2026-08-20 實錯:subagent 吃了那個預設,把 8 張測試卡寫進 leo 的真庫 (library=demo-real-verify)。當時 wiki 還教「任何自動化只准落在 leo21c」。 MSG exit 2