ข้ามไปยังเนื้อหา

กฎคุม — นโยบายเดียวสำหรับทุกการกระทำที่เปลี่ยนแปลงสถานะ

เอเจนต์ใน Control Center ลบไฟล์ พุชแบรนช์ เผยแพร่ pull request และเรียก API ภายนอกได้ แต่ละอย่างเป็นความเสี่ยงต่าง แบบ กัน และเป็นเวลานานที่มีเพียงอย่างเดียว — คำสั่งเชลล์ — ที่มีนโยบาย กฎคุมมีอยู่เพื่อตอบคำถามข้อเดียวอย่างสม่ำเสมอ สำหรับทุกการกระทำที่เปลี่ยนแปลงสถานะซึ่งเอเจนต์ทำได้ ไม่ว่าตัวรันหรือเครื่องมือใด จะเป็นผู้เสนอ: สิ่งนี้ได้รับอนุญาต ต้องมีการอนุมัติ หรือถูกปฏิเสธ?

มีเอนจินนโยบายเพียงหนึ่งเดียวเท่านั้น มันถูกเรียกใช้ ณ ทุกจุดที่การกระทำอาจออกจาก ระบบ: ทะเบียนเครื่องมือของฮาร์เนสในตัว ประตูตัวจัดส่ง MCP ฝั่งเซิร์ฟเวอร์ และการ เปลี่ยนแปลงจาก repo-op กฎที่เขียนครั้งเดียวจึงใช้กับทั้งรันไทม์ในตัวและเอเจนต์ CLI ภายนอกเหมือนกัน — ไม่มีเส้นทางที่สองที่เงียบกว่าอ้อมมันไป นโยบายคำสั่งแบบใช้แต่ bash แบบเดิมยังทำงานอยู่: กฎหนึ่งข้อจับคู่กับคำนำหน้าคำสั่งแทนที่จะเป็นประเภทการ กระทำ และทั้งสองรูปแบบอยู่ร่วมกันในที่เก็บเดียวกัน

อย่างไรก็ตาม เอนจินนี้ไม่ใช่สิ่งทดแทนแซนด์บ็อกซ์ของ OS และแซนด์บ็อกซ์ก็ไม่ใช่ สิ่งทดแทนของมัน ทั้งสองตอบคำถามต่างกัน: กฎคุมตัดสินว่าการเรียกที่ Control Center มองเห็นได้รับอนุญาตให้เกิดขึ้นเลยหรือไม่ ส่วนแซนด์บ็อกซ์จำกัดว่ากระบวนการที่เกิดขึ้น จะเอื้อมไปถึงที่ใดได้บ้าง cc_server ใช้แซนด์บ็อกซ์ในโฮสต์ที่มีแบ็กเอนด์ — แต่ Windows ไม่มีเลย และ Linux ต้องมี bwrap และ socat บนโฮสต์ เหล่านั้น กฎคุมคือชั้นบังคับใช้ชั้นนอกสุด ไม่ใช่นโยบายที่วางทับบนพื้น จึงควรรู้ไว้ เมื่อคุณตัดสินใจว่าจะผ่อนปรนแค่ไหน

เครื่องมือที่เปลี่ยนแปลงสถานะทุกตัวประกาศว่าตนเป็นสมาชิกของ ActionClass ใด จากชุดปิดของคลาสผลกระทบสิบสามคลาส ห้าคลาสถามก่อนโดยค่าเริ่มต้น ที่เหลืออนุญาต และกฎคือสิ่งที่เปลี่ยนมัน

คลาส ค่าเริ่มต้นในตัว ครอบคลุมอะไร
fileDelete ถามก่อน การลบไฟล์ (การเขียนแบบลบ, rm)
gitPush ถามก่อน การพุชไปยังรีโมตของ git
prCreate ถามก่อน การสร้าง pull request
prPublish ถามก่อน การเผยแพร่รีวิว, การรวม PR
vendorSyncWrite ถามก่อน การเขียนไปยังระบบตั๋วภายนอก (Linear, Jira, GitHub sync)
fileWriteOutsideWorktree อนุญาต การเขียนไฟล์นอก worktree ที่แยกออกมา
gitCommit อนุญาต การสร้างคอมมิตของ git
networkEgress อนุญาต การออกเครือข่าย (ดึงเว็บ, HTTP ตามอำเภอใจ)
secretAccess อนุญาต การอ่านความลับหรือข้อมูลรับรอง
packageInstall อนุญาต การติดตั้งแพ็กเกจ (npm, pip, pub, brew)
processSpawn อนุญาต การสปอว์นโพรเซส (เชลล์และทุกอย่างที่เรียกผ่านเชลล์)
workspaceMutation อนุญาต การเปลี่ยนโครงสร้างเวิร์กสเปซ (รีโป, สเปซ, เอเจนต์)
enclosureControl อนุญาต การควบคุมเอนโคลเจอร์ (ริก): บูต VM, ส่งอินพุตให้มัน

แปดคลาสที่อนุญาตโดยค่าเริ่มต้นถูกเลือกบนสมมติฐานว่ามีพื้นแซนด์บ็อกซ์อยู่ใต้มัน มันเป็นเช่นนั้นในโฮสต์ที่มีแบ็กเอนด์ และไม่เป็นเช่นนั้นบน Windows หรือบนเครื่อง Linux ที่ขาด bwrap และ socat — ดังนั้นถ้าคุณต้องการให้ fileWriteOutsideWorktree หรือ packageInstall ถูกกั้นที่นั่น นั่นคือกฎที่คุณต้องเขียนเอง

ชุดนี้ปิดโดยเจตนา เครื่องมือที่เปลี่ยนแปลงสถานะตัวใหม่ที่ประกาศคลาสไม่ได้จะ สอบไม่ผ่าน ratchet test อนุกรมวิธานจึงเติบโตได้ด้วยการตัดสินใจที่ชัดเจนและมี เหตุผลเท่านั้น — “การบานปลายของอนุกรมวิธานคือความตายของฟีเจอร์นี้” สิบสามคลาส เพียงพอที่จะแสดงนโยบายที่คนเขียนจริง (ไม่พุชโดยไม่ถาม, ไม่ลบนอก worktree, อนุญาตการอ่านเสมอ) และน้อยพอที่ UI การตั้งค่าจะยังเข้าใจได้

กฎหนึ่งข้อแมป (scope, ActionClass | command prefix) → allow | prompt | deny ขอบเขตซ้อนกันจากจำเพาะที่สุดไปหาไม่จำเพาะที่สุด:

สเปซ > เอเจนต์ > เวิร์กสเปซ > พรีเซ็ตโหมด > ค่าเริ่มต้นในตัว

การตัดสินให้ ความจำเพาะก่อน: ขอบเขตแรกที่มีกฎตรงกันเป็นตัวตัดสิน และการ ตัดสินหยุดที่ตรงนั้น การเข้มงวดที่สุดใช้เป็นตัวตัดสินเฉพาะเมื่อเสมอกันเท่านั้น — ระหว่างกฎสองข้อที่จำเพาะเท่ากันในขอบเขตเดียวกัน และเมื่อรวมหลายคลาสที่การกระทำ เดียวประกาศไว้ ภายในขอบเขตหนึ่ง กฎคำสั่งจับคู่กับคำนำหน้าคำสั่งที่ยาวที่สุด อนุญาตไม่ชนะปฏิเสธที่ใดเลย

การตัดสินสองอย่างมีหลักประกันที่แน่นอน:

  • คำถามที่ปิดเมื่อล้มเหลว prompt ที่ไม่มีผู้อนุมัติเชื่อมต่ออยู่จะกลายเป็น ปฏิเสธ คำถามที่ไม่มีคำตอบไม่เคยเป็นคำตอบว่าใช่
  • การรวมแบบเข้มงวดที่สุด การกระทำที่คร่อมหลายคลาสจะถูกรวมแบบเข้มงวดที่สุด เมื่อหลายคลาสถามก่อน การยืนยันครั้งเดียวจะแสดงรายการทั้งหมด

จงใจไม่มีตัวล็อกต่อเทิร์น ทุกการเรียกเครื่องมือตัดสินนโยบายใหม่ตั้งแต่ต้น การถามซ้ำสำหรับการกระทำเดิมจึงถูกจำกัดด้วย “จดจำการตัดสินนี้” ของผู้ปฏิบัติการเอง ไม่ใช่ด้วยสิ่งที่ผูกกับเทิร์น

กฎหนึ่งข้อจำกัด อาร์กิวเมนต์ ของการกระทำได้ ไม่ใช่แค่กริยาของมัน “พุชได้” กับ “พุชไปยัง feature/* ได้” เป็นข้ออ้างที่ต่างกัน และมีเพียงข้อที่สองเท่านั้นที่ เป็นการควบคุม — ประตูความสามารถที่อนุญาตเฉพาะกริยาเป็นรูปทรงที่อยู่เบื้องหลัง เหตุการณ์เอเจนต์ที่ถูกเปิดเผยส่วนใหญ่

ข้อจำกัดหนึ่งข้ออาจระบุ:

  • พาธ — รูปแบบ glob (repos/**, !**/.env)
  • ref — git ref พร้อมการปฏิเสธด้วย ! (['**', '!main'] อ่านว่า “ทุกแบรนช์ ยกเว้น main”)
  • โฮสต์ — ปลายทางเครือข่าย พร้อมไวลด์การ์ดซับโดเมน (*.internal)
  • คำสั่ง — คำนำหน้าคำสั่ง จับคู่ที่ขอบเขตคำ
  • เพดาน — maxCount / maxCents ทำให้ “ลบได้ถึง 50 ไฟล์” แสดงออกได้

คุณสมบัติสามอย่างเกิดจากการที่ไวยากรณ์ปิดและไม่มีลูป และทั้งสามคือประเด็น: ทุกกฎแสดงออกเป็นประโยคที่คุณอ่านกลับได้ ทุกการปฏิเสธระบุข้อจำกัดที่ตรงได้ และ นโยบายทั้งหมดแจกแจงได้ — คุณเห็นได้ว่าเอเจนต์ทำอะไรได้ ก่อน มันรัน

กฎสามข้อที่สำคัญ:

  • กฎที่มีข้อจำกัดจำเพาะกว่า กฎที่ไม่มีข้อจำกัดในขอบเขตเดียวกัน “ปฏิเสธการ พุชไป main” จึงชนะ “อนุญาตการพุช” โดยไม่มีใครต้องคิดเรื่องลำดับ
  • กฎแบบ จำกัด (ปฏิเสธ / ถามก่อน) มีผลเมื่อคำขอแตะค่าใด ๆ ที่มันระบุ กฎแบบ ผ่อนปรน (อนุญาต) มีผลเฉพาะเมื่อคำขออยู่ ทั้งหมด ภายในมัน การอ่านทั้งสอง แบบเดียวกันจะเปิดทางให้เอเจนต์ฟอกพาธต้องห้ามโดยพาเส้นทางที่ไม่ผิดอะไรไปด้วย ข้าง ๆ
  • กฎที่ระบุมิติที่คำขอไม่ได้กล่าวถึง จะไม่ถูกปรับใช้และไม่ถูกข้าม: มัน ยกระดับเป็นการขออนุมัติ การข้ามไปเงียบ ๆ จะทำให้กฎป้องกันแบรนช์ทุกข้อ ไร้ผลกับเครื่องมือที่ไม่รายงาน ref ของมัน การปฏิเสธเงียบ ๆ จะปฏิเสธทุกการพุช ที่ตัวสกัดอธิบายไม่ได้ เมื่อไม่มีผู้อนุมัติเชื่อมต่ออยู่ กฎปิดเมื่อล้มเหลวที่ มีอยู่จะเปลี่ยนการยกระดับนั้นเป็นการปฏิเสธ

กฎ commandPrefix จับคู่ที่ขอบเขตคำ: git push ครอบ git push origin main และไม่ครอบ git pushx มัน ไม่ ปรับรูปเชลล์ git push (เว้นวรรคสองครั้ง), cd sub && git push และ GIT_DIR=… git push เป็นสตริงที่ต่างกันทั้งหมด และ ไม่มีอันใดตรงกับกฎเกี่ยวกับ git push

นั่นคือข้อจำกัดจริง และมันถูกระบุไว้แทนที่จะกลบเกลื่อน: กฎคำสั่งเป็นความสะดวก ของผู้ปฏิบัติการในการจัดรูปพฤติกรรมปกติของเอเจนต์ ไม่ใช่ ขอบเขตต่อเอเจนต์ที่ เป็นปรปักษ์ ขอบเขตคือคลาสผลกระทบ (ซึ่งเครื่องมือประกาศและสะกดใหม่ไม่ได้) ตาข่าย คำสั่งของโหมดที่อยู่ใต้ที่เก็บนโยบาย และแซนด์บ็อกซ์ของ OS ที่อยู่ใต้ทั้งสอง กฎคำสั่งที่ตัดสินเป็น allow ไม่เคยทำให้สองอย่างนั้นหลวมขึ้น — ตาข่ายโหมดรัน หลังจากนั้น และ deny ของมันเป็นที่สุด

การตอบคำถามด้วย “จดจำการตัดสินนี้” เขียนกฎนโยบายจริง: มีขอบเขต (สเปซ / เอเจนต์ / เวิร์กสเปซ), มีข้อจำกัดที่อาร์กิวเมนต์ (การอนุมัติพุชไปยัง feature/login ให้สิทธิ์ feature/** ไม่ใช่ main) และ เพิกถอนตัวเอง ตาม TTL ที่ระบุชัด การตอบว่าใช่สำหรับสองสามชั่วโมงข้างหน้าไม่เหมือนกับการเขียนนโยบายถาวรของคุณใหม่ เงียบ ๆ และกฎที่มันเขียนจะลงในที่เก็บเดียวกันโดยมีแหล่งที่มาเป็น remembered และวันหมดอายุ จึงเพิกถอนก่อนกำหนดได้

แต่ละกฎมีระดับหนึ่ง:

  • advisory — อนุญาต แต่บันทึกว่ากฎตรง นี่คือเส้นทางการนำไปใช้: เปิดกฎเข้มใน โหมด advisory อ่านเส้นทางการตรวจสอบหนึ่งสัปดาห์เพื่อดูว่ามัน จะ บล็อกอะไร บ้าง แล้วเลื่อนขั้นมัน หากไม่มีระดับนี้ นโยบายเข้มจะไม่ถูกเปิดใช้เลย
  • soft — ปฏิเสธ แต่ผู้ที่ถือสิทธิ์เขียนทับเขียนทับได้ พร้อมเหตุผลที่บันทึกไว้ การเขียนทับนั้นเองก็เป็นเหตุการณ์ที่ถูกตรวจสอบ
  • hard (ค่าเริ่มต้น) — ปฏิเสธ ไม่มีสิ่งใดเขียนทับได้

ผู้ปฏิบัติการติดตั้งสามารถปักหมุดนโยบายที่ผู้ดูแลเวิร์กสเปซคนใดก็ทำให้หลวมขึ้น ไม่ได้ กฎที่จัดการจากส่วนกลางอยู่ฝั่งเซิร์ฟเวอร์และถูกรวมแบบ เข้มงวดที่สุด กับเชนของแต่ละเวิร์กสเปซ — ไม่ใช่ขอบเขตที่อยู่หัวเชน เพราะ allow ที่จัดการจาก ส่วนกลางซึ่งอยู่หัวเชนจะเปิดให้การติดตั้งเขียนทับการปฏิเสธของเวิร์กสเปซ ซึ่ง ตรงข้ามกับจุดประสงค์ของการบีบ กฎที่จัดการจากส่วนกลางจึงทำได้เพียงเข้มขึ้นเสมอ

การตั้ง CC_SERVER_MANAGED_POLICY ให้ชี้ไปที่ไฟล์ JSON มีอำนาจเหนือกฎที่จัดเก็บ ไว้ทั้งหมด นี่คือสิ่งที่ทำให้ผู้ปฏิบัติการปักหมุดท่าทีที่ UI ผู้ดูแลใด ๆ พลิกไม่ได้ — คำตอบของคำถาม “ฉันหยุดนักพัฒนาของฉันจากการปิดการควบคุมความปลอดภัยได้ไหม?”

ปุ่มปรับความเป็นอิสระเป็นโปรไฟล์ ไม่ใช่ระบบคู่ขนาน

หัวข้อที่มีชื่อว่า “ปุ่มปรับความเป็นอิสระเป็นโปรไฟล์ ไม่ใช่ระบบคู่ขนาน”

ปุ่มปรับความเป็นอิสระต่อสเปซเป็นโปรไฟล์ที่มีชื่อบนที่เก็บนโยบายเดียวกันนี้ ไม่ใช่กลไกที่สอง การตั้งค่าสามอย่างของมันถูกจัดเก็บและส่งเป็น proposeOnly, actWithApproval และ actFreely (รูปแบบที่มีขีดกลางเป็นถ้อยคำของ UI เท่านั้น และ autonomy.setForSpace ปฏิเสธอย่างอื่น) การปล่อยให้ไม่ได้ตั้งมีพฤติกรรม เหมือน actWithApproval

แต่ละอย่างทำอะไรกับการตัดสินที่ได้:

  • proposeOnly ปฏิเสธเครื่องมือที่มีประตูกั้นทุกตัว เอเจนต์ยังคิดและตอบได้ แต่ลงมือไม่ได้ และข้อความของมันบอกเหตุผล
  • actWithApproval (และเมื่อไม่ได้ตั้ง) คือประตูอนุมัติแบบปิดเมื่อล้มเหลวที่ อธิบายข้างต้น
  • actFreely อนุมัติล่วงหน้าทุกอย่างที่ไม่ได้ตัดสินเป็น deny แบบ hard

ข้อสุดท้ายควรถูกระบุตรง ๆ มากกว่าทำให้เบาลง: ภายใต้ actFreely การตัดสินที่เป็น prompt ไม่ ถูกยกระดับไปยังผู้อนุมัติ — มันถูกอนุญาตและไม่มีใครถูกถาม มีเพียงกฎ deny ที่ชัดเจนเท่านั้นที่รอดจากปุ่มปรับ ดังนั้น actFreely เป็นการ มอบความเป็นอิสระโดยเจตนา ไม่ใช่การตั้งค่าตามความสะดวก และกฎปิดเมื่อล้มเหลว ปกป้องกรณีที่มันสำคัญพอดี (actWithApproval ซึ่งเป็นค่าเริ่มต้น)

การมอบหมายระหว่างเอเจนต์ถูกป้องกันที่จุดที่ต้องผ่าน delegate_task ด้วยการ์ด ทั้งสี่: เพดานความลึก (ค่าเริ่มต้น 3), การตรวจจับวงจร, เพดานความเป็นอิสระ (ผู้รับมอบหมายลงมือด้วยความเป็นอิสระมากกว่าผู้มอบหมายไม่ได้เลย — สิทธิ์พิเศษ ไม่สามารถถูกฟอกด้วยการยกงานให้เอเจนต์ที่อิสระกว่า) และ กรอบงบประมาณ (การมอบหมายตัดจากงบประมาณที่เหลือของผู้มอบหมายและสร้างเพิ่มไม่ได้) แต่ละอย่าง ถูกปฏิเสธอย่างชัดเจน พร้อมเหตุผลของการ์ดที่ส่งกลับไปให้เอเจนต์ตามเดิม

ไม่ใช่ทุกรันไทม์ที่เปิดให้ Control Center สกัดทุกการกระทำ เอเจนต์ CLI ภายนอก รันเครื่องมือบางตัวแบบเนทีฟ ในโปรเซสเดียวกัน ซึ่งไม่มีประตูใดนั่งอยู่ได้ แทนที่ จะแกล้งทำเป็นอื่น แต่ละอะแดปเตอร์จึงมี เมทริกซ์ความซื่อสัตย์: ต่ออะแดปเตอร์ ว่า Control Center กรองพื้นผิวเครื่องมือ สกัดการเรียกเครื่องมือ สังเกตสัญญาการ จบงาน มองเห็นเครื่องมือเนทีฟของตัวรัน และว่าเครื่องมือในโปรเซสเดียวกันถูกครอบ ด้วยโปรไฟล์แซนด์บ็อกซ์หรือไม่

สองรายการควรค่าแก่การอ่านก่อนที่คุณจะเชื่อการตั้งค่าใด ๆ ฮาร์เนสในตัวประกาศว่า เครื่องมือไฟล์ในโปรเซสของมัน ไม่ ถูกแซนด์บ็อกซ์ — พื้นผิวเครื่องมือและ เอนจินนโยบายนี้คือขอบเขตระบบไฟล์เดียวที่มันมี Claude Code ประกาศว่าการเรียก เครื่องมือของมันเอง สกัดไม่ได้ เพราะมันถูกเรียกด้วย --dangerously-skip-permissions; Control Center เห็นการเรียก mcp__* ของมัน และบรรทัดคำสั่ง Bash ผ่านฮุก ส่วนเครื่องมืออ่าน เขียน และแก้ไขของมันเองรันแบบ ไม่ถูกเห็น

หน้าจอการตั้งค่าแสดงเมทริกซ์นี้ เพราะความล้มเหลวที่ไม่อาจให้อภัยได้อย่างเดียว สำหรับระบบกฎคุมคือการอ้างความครอบคลุมที่ไม่มีอยู่

กฎคุมอยู่ที่ การตั้งค่า → เวิร์กสเปซ → สิทธิ์ของเอเจนต์ (/workspaces/<id>/settings/workspace/permissions) หน้าจอมีเมทริกซ์นโยบายที่ ใช้เขียนกฎ เทมเพลตนโยบาย ตัวตรวจแบบสมมติ เส้นทางการตรวจสอบการอนุญาต และ เมทริกซ์ความซื่อสัตย์ของอะแดปเตอร์

ตัวตรวจรับคลาสการกระทำ (หรือคำสั่ง) และขอบเขต แล้วแสดงว่ากฎใดตัดสินและเพราะ อะไร เพราะการตัดสินบริสุทธิ์และกำหนดได้แน่นอน — การกระทำเดิม ขอบเขตเดิม คำตอบ เดิม — คำตอบของตัวตรวจคือคำตอบที่เอเจนต์จะได้รับ