رفتن به محتوا

گاردریل‌ها — یک سیاست برای هر اقدام تغییردهنده

عامل‌ها در 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) قرار دارند. این صفحه ماتریس سیاست را دارد که قواعد در آن نوشته می‌شوند، قالب‌های سیاست، کاوشگر «اگر…؟»، رد حسابرسی مجوز و ماتریس صداقت آداپتور.

کاوشگر یک صنف اقدام (یا یک فرمان) و یک محدوده می‌گیرد و نشان می‌دهد کدام قاعده تصمیم گرفت و چرا. چون تعیین خالص و قطعی است — همان اقدام، همان محدوده، همان تصمیم — پاسخ کاوشگر همان پاسخی است که عامل خواهد گرفت.