گاردریلها — یک سیاست برای هر اقدام تغییردهنده
عاملها در Control Center میتوانند فایل حذف کنند، شاخهها را push کنند، pull request منتشر کنند و APIهای بیرونی را صدا بزنند. هر یک از اینها نوع متفاوتی از ریسک است و مدتی طولانی تنها یکی از آنها — فرمانهای shell — سیاست داشت. گاردریلها وجود دارند تا یک پرسش را به شکلی یکسان، برای هر اقدام تغییردهندهای که یک عامل میتواند انجام دهد و فارغ از اینکه کدام زمان اجرا یا ابزار پیشنهادش میدهد، پاسخ دهند: آیا این مجاز است، به تأیید نیاز دارد، یا رد میشود؟
یک موتور، همهٔ گلوگاهها
بخش با عنوان “یک موتور، همهٔ گلوگاهها”دقیقاً یک موتور سیاست وجود دارد. در هر نقطهای که یک اقدام میتواند از سامانه بیرون بزند از آن مشورت گرفته میشود: رجیستری ابزار هارنس داخلی، دروازهٔ توزیعکنندهٔ MCP سمت سرور و تغییرات عملیات مخزن. پس قاعدهای که یک بار نوشته میشود هم بر زمان اجرای داخلی و هم بر CLIهای عامل بیرونی اعمال میشود — مسیر دومی، آرامتر، که دورش بزند وجود ندارد. سیاست فرمان قدیمی که فقط مخصوص bash بود هنوز کار میکند: یک قاعده میتواند بهجای صنف اقدام با پیشوند یک فرمان مطابقت کند و این دو شکل در همان مخزن کنار هم میمانند.
با این حال، موتور جایگزین سندباکس سیستمعامل نیست و سندباکس هم جایگزین آن
نیست. این دو به پرسشهای متفاوتی پاسخ میدهند: گاردریلها تعیین میکنند
که آیا فراخوانیای که Control Center میبیند اصلاً اجازهٔ رخ دادن دارد، و
سندباکس محدود میکند که فرایند برآمده تا کجا میتواند برسد. cc_server
سندباکس را جایی اعمال میکند که میزبان بکاندی عرضه کند — اما Windows
هیچ بکاندی عرضه نمیکند و Linux به bwrap و socat
نیاز دارد. روی آن
میزبانها گاردریلها بیرونیترین لایهٔ اعمالاند، نه سیاستی که روی یک کف
نشسته باشد؛ و این را بدانید وقتی تصمیم میگیرید چقدر تساهل کنید.
ActionClass: طبقهبندیای که عمداً بسته است
بخش با عنوان “ActionClass: طبقهبندیای که عمداً بسته است”هر ابزار تغییردهنده اعلام میکند به کدام ActionClassها تعلق دارد، از مجموعهای بسته با سیزده صنف اثر. پنج صنف از آنها بهصورت پیشفرض میپرسند؛ بقیه اجازه میدهند و این یک قاعده است که آن را عوض میکند.
| صنف | پیشفرض داخلی | چه چیزی را پوشش میدهد |
|---|---|---|
fileDelete |
ابتدا بپرس | حذف یک فایل (نوشتنی به شکل حذف، rm) |
gitPush |
ابتدا بپرس | push به یک ریموت git |
prCreate |
ابتدا بپرس | ساخت یک pull request |
prPublish |
ابتدا بپرس | انتشار یک بازبینی، ادغام یک PR |
vendorSyncWrite |
ابتدا بپرس | نوشتن در سامانهٔ بیرونی تیکت (Linear، Jira، همگامسازی GitHub) |
fileWriteOutsideWorktree |
اجازه | نوشتن فایلی بیرون از worktree جداشده |
gitCommit |
اجازه | ساخت یک کامیت git |
networkEgress |
اجازه | خروج به شبکه (واکشی وب، HTTP دلبخواه) |
secretAccess |
اجازه | خواندن یک راز یا اعتبارنامه |
packageInstall |
اجازه | نصب یک بسته (npm، pip، pub، brew) |
processSpawn |
اجازه | اجرای یک فرایند (shell و هر چیزی که shell را صدا میزند) |
workspaceMutation |
اجازه | تغییر ساختار فضای کاری (مخزنها، فضاها، عاملها) |
enclosureControl |
اجازه | راندن یک محفظه (ریگ): بوتکردن یک VM، فرستادن ورودی به آن |
هشت صنفی که پیشفرضشان اجازه است با این فرض انتخاب شدند که کف سندباکس
زیرشان اعمال میشود. این کار جایی که میزبان بکاند عرضه میکند انجام
میشود و روی Windows یا جعبهای Linux که bwrap و socat ندارد انجام
نمیشود — پس اگر میخواهید fileWriteOutsideWorktree یا packageInstall
آنجا دروازهدار شود، قاعدهای است که باید خودتان بنویسید.
این مجموعه عمداً بسته است. ابزار تغییردهندهٔ تازهای که هیچ صنفی اعلام نکند در آزمون قفل یکطرفه رد میشود، پس این طبقهبندی فقط با تصمیمی صریح و مدلّل میتواند رشد کند — «پخششدگی طبقهبندی، مرگ این قابلیت است.» سیزده صنف برای بیان سیاستهایی که مردم واقعاً مینویسند کافی است (هرگز بدون پرسیدن push نکن، هرگز بیرون از worktree حذف نکن، همیشه خواندن را مجاز بدان) و بهقدری کم است که رابط تنظیمات فهمپذیر بماند.
قواعد و تعیین محدوده
بخش با عنوان “قواعد و تعیین محدوده”یک قاعده (scope, ActionClass | command prefix) → allow | prompt | deny را
نقشه میکند. محدودهها از خاصترین به کمخاصترین تودرتو میشوند:
فضا > عامل > فضای کاری > پیشتنظیم حالت > پیشفرض داخلی
تعیین با تقدم خاصبودن انجام میشود: نخستین محدودهای که قاعدهای مطابق دارد تصمیم میگیرد و تعیین همانجا میایستد. محدودکنندهترین فقط شکستن تساوی است — میان دو قاعدهٔ همازحد خاص در یک محدوده و هنگام ترکیب چند صنفی که یک اقدام واحد اعلام میکند. درون یک محدوده، قاعدهٔ فرمان بر بلندترین پیشوند فرمان منطبق میشود. اجازه هیچجا از رد نمیبرد.
دو تصمیم تضمینهای سختی دارند:
- پرسشهای بسته در نبود پاسخ. یک
promptبدون تأییدکنندهٔ متصل به ردشده میرسد. پرسش بیپاسخ هرگز بله نیست. - ترکیب محدودکنندهترین. اقدامی که چند صنف را دربر میگیرد آنها را به محدودکنندهترین شکل ترکیب میکند؛ وقتی چند صنف میپرسند، یک تأیید همهٔ آنها را فهرست میکند.
عمداً هیچ قفلی در سطح هر نوبت وجود ندارد. هر فراخوانی ابزار سیاست را از صفر دوباره تعیین میکند، پس پرسیدن مکرر برای یک اقدام با «این تصمیم را به خاطر بسپار» خود اپراتور محدود میشود، نه با چیزی که به نوبت گره خورده باشد.
قواعد در سطح آرگومانها
بخش با عنوان “قواعد در سطح آرگومانها”یک قاعده میتواند آرگومانهای یک اقدام را محدود کند، نه فقط فعل آن.
«میتواند push کند» و «میتواند به feature/* push کند» دو ادعای متفاوت
اند و فقط دومی یک کنترل است — دروازهٔ تواناییای که تنها فعل را مجاز
میکند همان شکلی است که پشت بیشتر حوادث منتشرشدهٔ عاملها مینشیند.
یک محدودیت میتواند اینها را نام ببرد:
- مسیرها — الگوهای glob (
repos/**،!**/.env) - رفها — رفهای git، با نفی
!(['**', '!main']یعنی «هر شاخهای جز main») - میزبانها — مقصدهای شبکه، با نویسههای عام زیردامنه (
*.internal) - فرمانها — پیشوندهای فرمان، منطبق بر مرز واژه
- سقفها —
maxCount/maxCents، تا «حذف حداکثر 50 فایل» قابل بیان شود
سه ویژگی از بسته و بیحلقه نگهداشتن این دستور زبانی بیرون میافتد و هر سه همان مقصودند: هر قاعده بهصورت جملهای درمیآید که میتوانید بازخوانیاش کنید، هر رد میتواند محدودیتی را که منطبق شده نام ببرد، و کل سیاست قابل برشمردن است — میتوانید ببینید عامل پیش از اجرا چه کاری میتواند بکند.
سه قاعده که اهمیت دارند:
- یک قاعدهٔ مقید در همان محدوده از یک قاعدهٔ بیقید خاصتر است، پس
«رد push به
main» بدون اینکه کسی به ترتیب فکر کند «اجازهٔ push» را میبرد. - قاعدهٔ محدودکننده (رد / پرسش) وقتی اعمال میشود که درخواست هر مقداری را که نام برده لمس کند؛ قاعدهٔ مجاز (اجازه) فقط وقتی اعمال میشود که درخواست تماماً درون آن باشد. خواندن هر دو به یک شکل به عامل اجازه میدهد مسیری ممنوع را با کنار هم گذاشتن مسیری بیگناه تطهیر کند.
- قاعدهای که وجهی را نام میبرد که درخواست چیزی دربارهاش نمیگوید نه اعمال میشود و نه نادیده گرفته: به یک تأیید ارتقا مییابد. نادیده گرفتن خاموش هر قاعدهٔ شاخهٔ محافظتشده را در برابر ابزاری که رف خود را گزارش نمیکند بیاثر میکند؛ رد خاموش هم هر pushی را که استخراجکننده نمیتواند توصیف کند رد میکند. با نبود تأییدکنندهٔ متصل، قاعدهٔ بستهدر-نبود-پاسخ موجود آن ارتقا را به یک رد تبدیل میکند.
قاعدهٔ فرمان چه چیزی را میگیرد و چه چیزی را نه
بخش با عنوان “قاعدهٔ فرمان چه چیزی را میگیرد و چه چیزی را نه”یک قاعدهٔ commandPrefix بر مرز واژه منطبق میشود: git push شامل
git push origin main میشود و نه git pushx. این قاعده shell را نرمال
نمیکند. git push (دو فاصله)، cd sub && git push و
GIT_DIR=… git push رشتههای متفاوتیاند و هیچکدام با قاعدهای دربارهٔ
git push منطبق نمیشود.
این یک حد واقعی است و گفته میشود، نه لاپوشانی: قواعد فرمان راحتیای برای
اپراتورند تا رفتار عادی عامل را شکل دهد، نه مرزی در برابر عاملی
خصمانه. مرزها صنفهای اثر هستند (که ابزار اعلامشان میکند و نمیتواند
بازنویسیشان کند)، تور فرمان حالت در زیر مخزن سیاست، و سندباکس سیستمعامل
در زیر هر دو. قاعدهٔ فرمانی که به allow میرسد هیچیک از آن دو را شل
نمیکند — تور حالت پس از آن اجرا میشود و deny آن قطعی است.
تأییدهای ماندگار
بخش با عنوان “تأییدهای ماندگار”پاسخ دادن به یک پرسش با «این را به خاطر بسپار» یک قاعدهٔ سیاست واقعی
مینویسد: دارای محدوده (فضا / عامل / فضای کاری)، مقید به آرگومان
(تأیید push به feature/login دسترسی feature/** را میدهد، نه main)
و خودلغو پس از یک TTL صریح. بله گفتن برای چند ساعت آینده همان بازنویسی
آرام سیاست دائمی شما نیست، و قواعدی که مینویسد با منشأ remembered و یک
انقضا در همان مخزن مینشینند، پس میتوان آنها را پیش از موعد لغو کرد.
سطوح اعمال
بخش با عنوان “سطوح اعمال”هر قاعده یکی از اینها را حمل میکند:
- advisory — اجازه میدهد، اما ثبت میکند که قاعده منطبق شده. این مسیر پذیرش است: یک قاعدهٔ سختگیر را در حالت advisory منتشر کنید، یک هفته رد حسابرسی را بخوانید تا دقیقاً ببینید چه چیزی را مسدود میکرد، سپس ارتقایش دهید. بدون این رده، سیاستهای سختگیر هرگز روشن نمیشوند.
- soft — رد میکند، با امکان عبور برای کسی که مجوز عبور دارد، همراه با توجیهی ثبتشده. خود عبور یک رویداد حسابرسیشده است.
- hard (پیشفرض) — رد میکند. هیچ چیز از آن عبور نمیکند.
لایهٔ مدیریتشده
بخش با عنوان “لایهٔ مدیریتشده”اپراتور نصب میتواند سیاستی را سنجاق کند که هیچ مدیر فضای کاری نتواند
شلش کند. قواعد مدیریتشده سمت سرور زندگی میکنند و به شکل
محدودکنندهترین با زنجیرهٔ خود هر فضای کاری ادغام میشوند — آنها
محدودهای در سر زنجیره نیستند، چون یک allow مدیریتشده در سر زنجیره به
نصب اجازه میداد رد یک فضای کاری را باطل کند، یعنی عکس کاری که یک گیره
برای آن ساخته شده. پس یک قاعدهٔ مدیریتشده فقط میتواند سختتر کند.
تنظیم CC_SERVER_MANAGED_POLICY روی یک فایل JSON کاملاً بر قواعد
ذخیرهشده اولویت مییابد، و همین است که به اپراتور اجازه میدهد وضعیتی را
سنجاق کند که هیچ رابط مدیریتی نمیتواند عوضش کند — پاسخ به «آیا میتوانم
جلوی توسعهدهندههایم را بگیرم که کنترلهای ایمنی را خاموش کنند؟».
درجهٔ خودمختاری یک نمایه است، نه یک سامانهٔ موازی
بخش با عنوان “درجهٔ خودمختاری یک نمایه است، نه یک سامانهٔ موازی”درجهٔ خودمختاری هر فضا یک نمایهٔ نامدار روی همین مخزن سیاست است، نه
سازوکاری دوم. سه تنظیم آن بهصورت proposeOnly، actWithApproval و
actFreely ذخیره و فرستاده میشوند (شکلهای خطدار فقط متن رابطاند و
autonomy.setForSpace هر چیز دیگری را رد میکند). ترک آن بدون مقدار
مانند actWithApproval رفتار میکند.
اثر هر یک بر تصمیمی که تعیین شده:
proposeOnlyهر ابزار دروازهدار را یکسره رد میکند. عامل هنوز میتواند استدلال کند و پاسخ دهد؛ فقط نمیتواند عمل کند و پیامش میگوید چرا.actWithApproval(و بیمقدار) همان دروازهٔ تأیید بستهدر-نبود-پاسخ است که بالا توصیف شد.actFreelyهر چیزی را که به یکdenyسخت نرسیده باشد پیشتأیید میکند.
این آخری سزاوار آن است که صریح گفته شود، نه تلطیف: زیر 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) قرار دارند. این صفحه
ماتریس سیاست را دارد که قواعد در آن نوشته میشوند، قالبهای سیاست، کاوشگر
«اگر…؟»، رد حسابرسی مجوز و ماتریس صداقت آداپتور.
کاوشگر یک صنف اقدام (یا یک فرمان) و یک محدوده میگیرد و نشان میدهد کدام قاعده تصمیم گرفت و چرا. چون تعیین خالص و قطعی است — همان اقدام، همان محدوده، همان تصمیم — پاسخ کاوشگر همان پاسخی است که عامل خواهد گرفت.
مفاهیم مرتبط
بخش با عنوان “مفاهیم مرتبط”- سندباکس و امنیت: چه چیزی واقعاً اجرای یک عامل را محدود میکند و کف سیستمعامل زیر این موتور تا کجا میرسد
- چندنفره — هویت، عضویت و حضور: فضاها، هویتها و جایی که درجهٔ خودمختاری میزید
- پیکربندی گاردریلها: نوشتن و آزمودن قواعد در عمل