Lewati ke konten

Pagar pengaman — satu kebijakan untuk setiap tindakan yang mengubah keadaan

Agen di Control Center dapat menghapus file, mem-push branch, memublikasikan pull request, dan memanggil API eksternal. Setiap tindakan itu adalah jenis risiko yang berbeda, dan selama ini hanya salah satunya — perintah shell — yang punya kebijakan. Pagar pengaman hadir untuk menjawab satu pertanyaan secara seragam, untuk setiap tindakan yang mengubah keadaan yang dapat dilakukan agen, apa pun runtime atau alat yang mengusulkannya: apakah ini diizinkan, perlu persetujuan, atau ditolak?

Hanya ada satu mesin kebijakan. Mesin itu dikonsultasikan di setiap titik tempat sebuah tindakan dapat meninggalkan sistem: registri alat milik harness bawaan, gerbang dispatcher MCP di sisi server, dan mutasi repo-op. Aturan yang ditulis sekali karena itu berlaku untuk runtime bawaan maupun CLI agen eksternal — tidak ada jalur kedua yang lebih sunyi untuk mengelilinginya. Kebijakan perintah lama yang hanya menangani bash masih berfungsi: sebuah aturan dapat mencocokkan prefiks perintah sebagai ganti kelas aksi, dan kedua bentuk itu hidup berdampingan di penyimpanan yang sama.

Mesin ini bukan pengganti sandbox OS, dan sandbox bukan penggantinya. Keduanya menjawab pertanyaan yang berbeda: pagar pengaman memutuskan apakah panggilan yang dapat dilihat Control Center boleh terjadi sama sekali, sedangkan sandbox membatasi ke mana proses yang dihasilkan boleh menjangkau. cc_server menerapkan sandbox di tempat host menyediakan backend — tetapi Windows tidak menyediakannya dan Linux memerlukan bwrap dan socat. Di host seperti itu, pagar pengaman adalah lapisan penegakan terluar, bukan kebijakan yang berdiri di atas dasar, dan itu perlu diketahui saat Anda memutuskan seberapa permisif ingin bersikap.

Setiap alat yang mengubah keadaan mendeklarasikan ActionClasses mana yang diikutinya, dari himpunan tertutup berisi tiga belas kelas efek. Lima di antaranya meminta persetujuan secara bawaan; sisanya mengizinkan, dan aturanlah yang mengubahnya.

Kelas Default bawaan Cakupannya
fileDelete prompt Menghapus file (penulisan berjenis hapus, rm)
gitPush prompt Push ke remote git
prCreate prompt Membuat pull request
prPublish prompt Memublikasikan tinjauan, menggabungkan PR
vendorSyncWrite prompt Menulis ke pelacak tiket eksternal (Linear, Jira, sinkronisasi GitHub)
fileWriteOutsideWorktree allow Menulis file di luar pohon kerja yang terisolasi
gitCommit allow Membuat commit git
networkEgress allow Lalu lintas keluar jaringan (pengambilan web, HTTP sembarang)
secretAccess allow Membaca secret atau kredensial
packageInstall allow Menginstal paket (npm, pip, pub, brew)
processSpawn allow Menjalankan proses (shell dan apa pun yang memanggil shell)
workspaceMutation allow Mengubah struktur ruang kerja (repo, space, agen)
enclosureControl allow Mengendalikan enclosure (rig): menyalakan VM, mengirimkan masukan kepadanya

Delapan kelas yang bawaannya allow dipilih dengan asumsi dasar sandbox berlaku di bawahnya. Asumsi itu benar di tempat host menyediakan backend, dan tidak berlaku di Windows atau di mesin Linux yang tidak memiliki bwrap dan socat — jadi jika Anda ingin fileWriteOutsideWorktree atau packageInstall dibatasi di sana, aturan itulah yang harus Anda tulis sendiri.

Himpunan ini ditutup dengan sengaja. Alat pengubah baru yang tidak mendeklarasikan kelas apa pun gagal dalam uji ratchet, sehingga taksonomi hanya dapat bertambah melalui keputusan yang eksplisit dan beralasan — “taksonomi yang melebar adalah kematian fitur ini.” Tiga belas kelas cukup untuk menyatakan kebijakan yang benar-benar ditulis orang (jangan pernah push tanpa bertanya, jangan pernah menghapus di luar pohon kerja, selalu izinkan pembacaan) dan cukup sedikit agar UI pengaturan tetap dapat dipahami.

Sebuah aturan memetakan (scope, ActionClass | command prefix) → allow | prompt | deny. Cakupan bersarang dari yang paling spesifik ke yang paling umum:

space > agen > ruang kerja > preset mode > default bawaan

Penyelesaian mengutamakan spesifisitas: cakupan pertama yang memiliki aturan yang cocoklah yang memutuskan, dan penyelesaian berhenti di situ. Paling ketat hanya menjadi penentu seri — di antara dua aturan yang sama-sama spesifik dalam satu cakupan, dan saat menggabungkan beberapa kelas yang dideklarasikan satu tindakan. Dalam satu cakupan, aturan perintah dicocokkan pada prefiks perintah terpanjang. Allow tidak pernah mengalahkan deny di mana pun.

Dua keputusan membawa jaminan keras:

  • Prompt fail-closed. Sebuah prompt tanpa pemberi persetujuan yang terhubung diselesaikan menjadi ditolak. Pertanyaan yang tidak dijawab tidak pernah berarti ya.
  • Kombinasi paling ketat. Tindakan yang mencakup beberapa kelas digabungkan secara paling ketat; ketika beberapa kelas meminta persetujuan, satu konfirmasi mencantumkan semuanya.

Tidak ada status yang menempel per giliran, dan itu memang disengaja. Setiap panggilan alat menyelesaikan ulang kebijakan dari nol, sehingga permintaan persetujuan berulang untuk tindakan yang sama dibatasi oleh “ingat keputusan ini” milik operator sendiri, bukan oleh apa pun yang terikat pada satu giliran.

Sebuah aturan dapat membatasi ARGUMEN suatu tindakan, bukan hanya verbanya. “Boleh push” dan “boleh push ke feature/*” adalah klaim yang berbeda, dan hanya klaim kedua yang merupakan kontrol — gerbang kemampuan yang hanya mengizinkan verbanya adalah bentuk di balik sebagian besar insiden agen yang dipublikasikan.

Sebuah batasan dapat menyebutkan:

  • path — pola glob (repos/**, !**/.env)
  • ref — ref git, dengan negasi ! (['**', '!main'] terbaca sebagai “branch apa pun kecuali main”)
  • host — tujuan jaringan, dengan wildcard subdomain (*.internal)
  • perintah — prefiks perintah, dicocokkan pada batas kata
  • plafon — maxCount / maxCents, sehingga “hapus hingga 50 file” dapat dinyatakan

Tiga properti muncul dari menjaga tata bahasa tetap tertutup dan tanpa perulangan, dan ketiganya adalah intinya: setiap aturan tampil sebagai kalimat yang dapat Anda baca kembali, setiap penolakan dapat menyebut batasan yang cocok, dan seluruh kebijakan dapat dienumerasi — Anda dapat melihat apa yang boleh dilakukan agen sebelum agen itu berjalan.

Tiga aturan yang penting:

  • Aturan yang dibatasi lebih spesifik daripada aturan tanpa batasan pada cakupan yang sama, sehingga “tolak push ke main” mengalahkan “izinkan push” tanpa perlu ada yang memikirkan urutan.
  • Aturan restriktif (deny / prompt) berlaku ketika permintaan menyentuh nilai apa pun yang disebutnya; aturan permisif (allow) hanya berlaku ketika permintaan seluruhnya berada di dalamnya. Membaca keduanya dengan cara yang sama akan membuat agen dapat menyamarkan path terlarang dengan menyelipkan path yang tidak berbahaya di sampingnya.
  • Aturan yang menyebut faset yang tidak disinggung permintaan tidak diterapkan dan juga tidak dilewati: aturan itu naik menjadi permintaan persetujuan. Melewatinya secara diam-diam akan membuat setiap aturan branch terlindungi menjadi tidak berguna terhadap alat yang tidak melaporkan ref-nya; menolak secara diam-diam akan menolak setiap push yang tidak dapat dideskripsikan ekstraktor. Dengan tidak ada pemberi persetujuan yang terhubung, aturan fail-closed yang ada mengubah eskalasi itu menjadi penolakan.

Apa yang dapat dan tidak dapat ditangkap aturan perintah

Bagian berjudul “Apa yang dapat dan tidak dapat ditangkap aturan perintah”

Aturan commandPrefix dicocokkan pada batas kata: git push mencakup git push origin main dan bukan git pushx. Aturan ini tidak menormalkan shell. git push (dua spasi), cd sub && git push, dan GIT_DIR=… git push adalah string yang berbeda-beda dan tidak satu pun cocok dengan aturan tentang git push.

Itu batas yang nyata dan dinyatakan terang-terangan, bukan ditutup-tutupi: aturan perintah adalah kemudahan bagi operator untuk membentuk perilaku agen biasa, bukan batas terhadap agen yang bermusuhan. Batasnya adalah kelas efek (yang dideklarasikan sebuah alat dan tidak dapat disebutnya dengan nama lain), jaring perintah mode di bawah penyimpanan kebijakan, dan sandbox OS di bawah keduanya. Aturan perintah yang diselesaikan menjadi allow tidak pernah melonggarkan keduanya — jaring mode berjalan setelahnya dan deny-nya bersifat final.

Menjawab prompt dengan “ingat ini” menulis aturan kebijakan yang sungguhan: bercakupan (space / agen / ruang kerja), dibatasi argumennya (menyetujui push ke feature/login memberikan feature/**, bukan main) dan mencabut dirinya sendiri pada TTL eksplisit. Berkata ya untuk beberapa jam ke depan tidak sama dengan diam-diam menulis ulang kebijakan permanen Anda, dan aturan yang ditulisnya masuk ke penyimpanan yang sama dengan asal-usul remembered dan masa kedaluwarsa, sehingga dapat dicabut lebih awal.

Setiap aturan membawa satu tingkat:

  • advisory — izinkan, tetapi catat bahwa aturan cocok. Inilah jalur adopsi: terapkan aturan ketat dalam mode advisory, baca jejak audit selama sepekan untuk melihat persis apa yang akan diblokirnya, lalu naikkan statusnya. Tanpa tingkat ini, kebijakan ketat tidak akan pernah dinyalakan.
  • soft — tolak, dapat ditimpa oleh pemegang izin penimpaan, dengan pembenaran yang dicatat. Penimpaan itu sendiri adalah peristiwa yang diaudit.
  • hard (default) — tolak. Tidak ada yang dapat menimpanya.

Operator instalasi dapat memakukan kebijakan yang tidak dapat dilonggarkan admin ruang kerja mana pun. Aturan terkelola berada di sisi server dan digabungkan secara paling ketat dengan rantai milik masing-masing ruang kerja — aturan itu bukan cakupan di kepala rantai tersebut, karena allow terkelola di kepala rantai akan membuat instalasi menimpa deny sebuah ruang kerja, kebalikan dari tujuan sebuah pengunci. Karena itu, aturan terkelola hanya dapat memperketat.

Menyetel CC_SERVER_MANAGED_POLICY ke sebuah file JSON mengalahkan aturan yang tersimpan sepenuhnya; itulah yang memungkinkan operator memakukan postur yang tidak dapat dibalik oleh UI admin mana pun — jawaban untuk “bisakah saya mencegah pengembang saya menonaktifkan kontrol keamanan?”.

Dial otonomi adalah profil, bukan sistem paralel

Bagian berjudul “Dial otonomi adalah profil, bukan sistem paralel”

Dial otonomi per space adalah profil bernama di atas penyimpanan kebijakan yang sama, bukan mekanisme kedua. Ketiga pengaturannya disimpan dan dikirim sebagai proposeOnly, actWithApproval dan actFreely (bentuk bertanda hubung hanyalah prosa UI, dan autonomy.setForSpace menolak nilai lain apa pun). Jika dibiarkan tidak disetel, perilakunya sama dengan actWithApproval.

Apa yang dilakukan masing-masing terhadap keputusan yang sudah diselesaikan:

  • proposeOnly menolak setiap alat yang melewati gerbang secara langsung. Agen tetap dapat bernalar dan membalas; hanya saja ia tidak dapat bertindak dan pesannya menjelaskan alasannya.
  • actWithApproval (dan tidak disetel) adalah gerbang persetujuan fail-closed yang dijelaskan di atas.
  • actFreely menyetujui di muka apa pun yang tidak diselesaikan menjadi deny keras.

Poin terakhir itu pantas dinyatakan terus terang, bukan dilembutkan: di bawah actFreely sebuah keputusan prompt tidak dieskalasi ke pemberi persetujuan — keputusan itu diizinkan dan tidak ada yang ditanya. Hanya aturan deny eksplisit yang bertahan melewati dial ini. Jadi actFreely adalah pemberian otonomi yang disengaja, bukan pengaturan demi kenyamanan, dan aturan fail-closed justru melindungi kasus yang paling penting (actWithApproval, default).

Delegasi antaragen dijaga di titik penyempitan delegate_task oleh keempat penjaga: batas kedalaman (default 3), deteksi siklus, batas atas otonomi (delegat tidak akan pernah dapat bertindak dengan otonomi melebihi pemberi delegasinya — hak istimewa tidak dapat dicuci dengan menyerahkan pekerjaan kepada agen yang lebih bebas) dan batas anggaran (delegasi membebankan sisa anggaran pemberi delegasi dan tidak dapat mencetak anggaran baru). Masing-masing ditolak secara terang-terangan, dengan alasan dari penjaga dikembalikan kepada agen kata per kata.

Tidak setiap runtime memungkinkan Control Center mencegat setiap tindakan. CLI agen eksternal menjalankan sebagian alat secara native, in-process, di tempat yang tidak dapat dijangkau gerbang mana pun. Alih-alih berpura-pura sebaliknya, setiap adapter membawa matriks kejujuran: per adapter, apakah Control Center memfilter permukaan alat, mencegat panggilan alat, mengamati kontrak penyelesaian, dapat melihat alat native milik runner, dan apakah alat in-process tercakup profil sandbox.

Ada dua entri yang perlu dibaca sebelum Anda memercayai sebuah konfigurasi. Harness bawaan menyatakan bahwa alat file in-process-nya tidak di-sandbox — permukaan alat dan mesin kebijakan ini adalah satu-satunya batas sistem file yang dimilikinya. Claude Code menyatakan bahwa panggilan alatnya sendiri tidak dapat dicegat, karena ia dijalankan dengan --dangerously-skip-permissions; Control Center melihat panggilan mcp__*-nya dan, melalui hook, baris perintah Bash-nya, sedangkan alat baca, tulis, dan edit miliknya berjalan tanpa terlihat.

Layar pengaturan menampilkan matriks ini, karena satu-satunya kegagalan yang tidak dapat dimaafkan bagi sistem pagar pengaman adalah mengklaim cakupan yang tidak ada.

Pagar pengaman berada di Pengaturan → Ruang kerja → Izin agen (/workspaces/<id>/settings/workspace/permissions). Layar ini memuat matriks kebijakan tempat aturan ditulis, templat kebijakan, uji “Bagaimana jika?”, jejak audit otorisasi, dan matriks kejujuran adapter.

Uji tersebut menerima kelas aksi (atau perintah) dan sebuah cakupan, lalu menunjukkan aturan mana yang memutuskan dan mengapa. Karena penyelesaiannya murni dan deterministik — aksi yang sama, cakupan yang sama, keputusan yang sama — jawaban dari uji itu adalah jawaban yang akan diterima agen.