تصور کنید در یک سازمان با ۱۴ هزار مخزن کد هستید و وقتی یک حفره امنیتی حیاتی پیدا میشود، نمیدانید دقیقاً چه کسی باید آن را تعمیر کند. این کابوس لجستیکی همان چیزی است که مهندسان گیتهاب برای حل آن دست به یک پاکسازی ساختاری زدند. مدیریت ۱۴,۰۰۰ مخزن داخلی زمانی که نتوانید تشخیص دهید مالک کد کیست، یک چالش لجستیکی غیرقابل تحمل است. گیتهاب این مشکل را با تبدیل «مالکیت مخزن» به یک ویژگی دست اول و اجباری برای هر پروژه در سازمانش حل کرد.
طبق گزارش منتشرشده در github.blog، گیتهاب مالکیت مخزن (Repository Ownership) را به یک ویژگی دست اول و اجباری برای تمام پروژههای سازمان تبدیل کرد. پیش از این تغییر، شرکت با شکاف بزرگی در پاسخگویی روبهرو بود؛ در حالی که سرویسهای عملیاتی مالک مشخصی داشتند، هزاران مخزن تیمی، ابزارهای داخلی و آزمایشهای شخصی در خلأ مطلق قرار داشتند. این ابهام بهخصوص در زمان شناسایی اسرار (Secret Scanning) — مثل کلیدهای امنیتی که بهاشتباه در کد قرار میگیرند — به یک ریسک حیاتی تبدیل میشد، چون تیمهای امنیتی راهی برای ارجاع هشدارها بدون ایجاد اختلال در جریان کاری نداشتند. در واقع، چرخش (Rotate) یک کلید امنیتی بدون دانستن مالک آن، اغلب بیش از حد ریسکی بود و باعث اختلال در جریانهای کاری میشد. این چالشهای نظارتی مشابه مواردی است که در auditing امنیتی تیمهای مهندسی برای مدیریت دسترسی به کدهای محرمانه مشاهده شده است.
همانطور که در تحلیلهای پیشین ما دربارهی استانداردهای حاکمیت داده در سازمانهای بزرگ اشاره کردیم، نبودِ متادیتا در مقیاس هزاران مخزن، مدیریت را غیرممکن میکند. در اوایل سال ۲۰۲۵، گیتهاب بیش از ۱۱ هزار مخزن غیربایگانیشده را شناسایی کرد که اکثریت قریب به اتفاق آنها فاقد مالک مشخص بودند. برای حل این مشکل، شرکت در بازهای کمتر از ۴۵ روز، عملیات اعتبارسنجی مالکیت را انجام داد و تقریباً ۸,۰۰۰ مخزن بلااستفاده را بایگانی کرد تا وضعیت واقعی داراییهای کد منعکس شود.
زمینه و ریشهی شکاف مالکیت
سالها بود که گیتهاب مالکیت سرویسهای مستقر را از طریق یک «کاتالوگ سرویس» (Service Catalog) داخلی رصد میکرد. این کاتالوگ یک نقشهبرداری غنی از متادیتا برای هر ورودی سرویس فراهم میکرد که شامل موارد زیر بود:
- مخزنی که سرویس در آن قرار داشت
- تیم مالک
- حامی اجرایی (Executive Sponsor)
- اطلاعات پشتیبانی
برای مثال، ورودی مربوط به اپلیکیشن «مالکیت مخزن» (Repo Ownership app)، تیم را github/repo-ownership-dev، نام را repo-ownership و نوع را moda لیست کرده بود و همچنین mrecachinas را به عنوان نگهدارنده (Maintainer) و stephanmiehe را به عنوان حامی اجرایی شناسایی میکرد. این دادهها امکان اجرای گردشکارهای حیاتیِ سرویس-محور مانند پاسخ به حوادث (Incident Response)، مسیریابی آنکال (On-call Routing)، مدیریت آسیبپذیریها و تعیین محدوده انطباق (Compliance Scoping) را فراهم میکرد.
اما مشکل این بود که این رابطه «چند به یک» بود: یک سرویس فقط میتوانست به یک مخزن متصل شود، اما یک مخزن میتوانست میزبان چندین سرویس باشد. این یعنی اگر از یک سرویس شروع میکردید، مالک را مییافتید، اما اگر از یک مخزن شروع میکردید، باید جستوجوی معکوس میکردید که فقط برای مخازنی که توسط یک سرویس پشتیبانی میشدند جواب میداد.
نتیجه این بود که حجم عظیمی از کدهای «بدون مالک» باقی ماند. این شکاف شامل مخازن تیمی، مخازن مستندات، ابزارهای داخلی، پروژههای تکشبه و آزمایشهای شخصی بود. یافتن مالکان برای این موارد نیازمند تلاش دستی بود: بررسی تاریخچه کامیتها، خواندن فایلهای README، پرسوجو در اسلک (Slack) یا حدس زدن بر اساس نام مخزن.
معماری فنی و پیادهسازی
گیتهب ایدهی ذخیره مالکیت در فایلهای README یا جداول اکسل مرکزی را رد کرد. در عوض، از ویژگیهای سفارشی گیتهاب (GitHub Custom Properties) — که شبیه به برچسبهای هوشمند برای دستهبندی سریع داراییهاست — برای ایجاد یک سیستم پرسوجویی سازمانیافته در سطح کل سازمان استفاده کرد. این رویکرد به آنها اجازه داد تا سیاستهای سازمانی و مجموعهقوانین (Rulesets) را به طور انتخابی و بر اساس نوع مالکیت اعمال کنند.
آنها دو ویژگی خاص تعریف کردند:
- ownership-type: یک فیلد تکانتخابی با سه مقدار:
- «کاتالوگ سرویس» (Service Catalog): برای مخازنی با تیم آنکال و چرخه حیات تعریفشده.
- «شناسه هابر» (Hubber Handle): برای افراد (گیتهاب کارمندانش را Hubbers مینامد)، مانند پروژههای شخصی یا آزمایشها.
- «تیم» (Team): برای مستندات مشترک یا ابزارهای داخلی.
- ownership-name: یک فیلد متنی برای شناسهی دقیق و منحصربهفرد مالک.
برای تضمین صحت دادهها، آنها یک لایهی اعتبارسنجی را از طریق یک اپلیکیشن گیتهاب (GitHub App) طراحی کردند که توسط یک کرونجاب کوبرنتیز (Kubernetes CronJob) پشتیبانی و اجرا میشد. یک گردشکار ساده در GitHub Actions کافی نبود، چون منطق اجرا به دسترسی همزمان به کاتالوگ سرویس، API گیتهاب و سایر سیستمهای داخلی نیاز داشت.
این اپلیکیشن در پذیرش فرمتها عمداً منعطف بود؛ مثلاً اگر کاربر به جای my-team مینوشت @my-team سیستم آن را میپذیرفت تا اصطکاک برای کاربر کم شود و فرآیند بدون دردسر پیش برود. اما در تایید هویت سختگیر بود: شناسههای Hubber باید واقعاً عضو سازمان میبودند، تیمها باید وجود داشتند و حداقل دو عضو داشتند و ورودیهای کاتالوگ سرویس باید در حال حاضر فعال میبودند.
استقرار و اجرای سختگیرانه
بر اساس مستندات گیتهاب، عملیات با یک همگامسازی دورهای از کاتالوگ سرویس داخلی به ویژگیهای سفارشی مخازن شروع شد. این پر کردن خودکار دادهها بلافاصله حدود ۱،۵۰۰ مخزن متصل به سرویس را پوشش داد و آنها را از لیست پاکسازی دستی خارج کرد.
برای باقی مخازن «بدون مالک»، شرکت یک مهلت ۳۰ روزه تعیین کرد. سیستم از یک جریان خاص پیروی میکرد: ابتدا یک اسکن مالکیت اولیه، سپس ایجاد یک Issue هشداردهنده در مخزن و در نهایت، بستن یا بایگانی خودکار مخازن در صورتی که پس از ۳۰ روز هنوز مالکی تعیین نشده باشد.
انتخاب «بایگانی» (Archive) به جای حذف، دلیل استراتژیک داشت؛ چون این فرآیند برگشتپذیر است و تخریبی نیست. وقتی مخزنی بایگانی میشود، فقطخوان (Read-only) میشود و اجرای GitHub Actions متوقف میگردد، اما هیچ دادهای پاک نمیشود. این کار باعث شد گیتهاب بتواند بایگانی را به طور گسترده اعمال کند بدون اینکه درباره هر مورد استثنایی بحث کند، زیرا هر کاربر میتوانست به راحتی مخزن را از حالت بایگانی خارج کرده و مالکیت لازم را تعیین کند تا فعالیت پروژه ادامه یابد.
درسهایی از شکستها
این مسیر بدون خطا نبود. تیم اولین اجرای CronJob را برای صبح شنبه برنامهریزی کرد با این فرض که دیده نشود. اما این یک اشتباه بود؛ چون گیتهاب یک شرکت توزیعشده جهانی است، همیشه کسی آنلاین است. مهندسان بلافاصله در اسلک فعال شدند تا بپرسند چرا Issueهایی در مخازنشان ظاهر شده که هشدار بایگانی میدهد.
دو حادثه داخلی غیرمعمول، نقاط ضعف طراحی را آشکار کرد:
حادثه اول: شکست در اطلاعرسانی. یک مخزن فعال بایگانی شد چون Issue هشدار توسط مالک نادیده گرفته شده بود. این مخزن توسط Datadog برای باز کردن Issueها به عنوان بخشی از یک گردشکار مانیتورینگ استفاده میشد. وقتی مخزن Read-only شد، Datadog نتوانست Issue ایجاد کند. یک سرویس مانیتورینگ داخلی این شکست را شناسایی کرد و تیمی که آنکال بود را خبر کرد، و آنها موضوع را به تیم مالکیت ارجاع دادند.
این ثابت کرد که صرفاً ایجاد Issue در یک مخزن کافی نیست. گیتهاب این مشکل را با @mention کردن مدیران مخزن و تخصیص (Assign) تمام کاربرانی که دسترسی Write داشتند به عنوان جایگزین در Issueهای مالکیت حل کرد تا دیده شدن هشدار تضمین شود.
حادثه دوم: قابلیت اطمینان دادهها و «مرز پایین». حادثه دوم مربوط به فساد دادهها بود. در حالی که سیستم در برابر قطع کامل کاتالوگ سرویس مقاوم بود، اما در برابر بازگشت دادههای قدیمی یا فاسد از کاتالوگ آمادگی نداشت. اگر اپلیکیشن به اشتباه تصور میکرد دستهای از مخازن ورودیهای کاتالوگ سرویس خود را از دست دادهاند، ممکن بود تعداد زیادی از مخازن معتبر را به صورت دستهجمعی بایگانی کند.
برای جلوگیری از این فاجعه، آنها یک «مرز پایین» (Low Water Mark) تعریف کردند. در هر اجرا، اپلیکیشن میشمارد که قصد دارد چه تعداد بایگانی و Issue ایجاد کند. اگر این تعداد از یک آستانه محافظهکارانه بیشتر شود، اپلیکیشن تماماً عملیات را متوقف کرده و یک مانیتور در Datadog را فعال میکند. علاوه بر این، اگر کاتالوگ سرویس در دسترس نباشد، جاب (Job) اعتبارسنجی کاتالوگ را نادیده میگیرد و فقط مواردی را بررسی میکند که میتواند به صورت مستقل تایید کند.
تداوم پوشش ۱۰۰ درصدی
برای جلوگیری از زوال موجودی و بازگشت به وضعیت قبلی، گیتهاب تعریف ویژگیهای مالکیت را در لحظه ایجاد مخزن اجباری کرد. این الزام در صفحه ایجاد مخزن، ابزارهای داخلی و تمام اتوماسیونها اعمال شده است.
پس از اتمام پاکسازی اولیه، آنها حلقه اجرا را سختگیرانهتر کردند. در حالی که مهلت اولیه ۳۰ روز بود، اکنون هر مخزنی که به هر نحوی الزام زمان ایجاد را دور بزند، ظرف یک ساعت شناسایی و علامتگذاری میشود.
هر نوع مالکیت با یک چرخه حیات خاص همسو است:
- کاتالوگ سرویس: اینها از چرخه حیات سرویس پیروی میکنند؛ وقتی یک سرویس منسوخ (Deprecated) شود، مخزن آن بایگانی میشود.
- تیمها: اینها پایدار هستند زیرا اعتبارسنجی شده است که حداقل دو عضو دارند.
- شناسههای فردی (Hubber Handles): اینها زمانی که کارمندی شرکت را ترک کند، نامعتبر میشوند. در این موارد، مخازن شخصی باید به طور پیشفرض بایگانی شوند. برای هر پروژه حیاتی، مالکیت باید به یک تیم یا سرویس منتقل شود.
این پاکسازی سیستماتیک، تعداد مخازن فعال را به حدود ۳,۰۰۰ مورد کاهش داد و تعداد مخازن بایگانیشده را از ۳,۰۰۰ به ۱۱,۰۰۰ رساند. بسیاری از این بایگانیها شامل نمونههای اولیه سال ۲۰۰۸ و پروژههای رهاشدهی هکاتونها بودند.
توصیههایی برای سایر سازمانها
برای سازمانهایی که میخواهند این مدل را با استفاده از ویژگیهای سفارشی گیتهاب پیاده کنند، رویکرد زیر توصیه میشود:
- یک تاکسونومی (طبقهبندی) تعریف کنید: تصمیم بگیرید که آیا به دستهبندی سرویسها، تیمها یا افراد نیاز دارید. دستههای شما ممکن است با گیتهاب متفاوت باشد.
- از ویژگیهای سطح سازمان استفاده کنید: یک فیلد
ownership-typeتکانتخابی و یک فیلد متنیownership-nameبرای قابلیت پرسوجو از طریق API تعریف کنید. - داراییهای موجود را همگامسازی کنید: پر کردن دادهها از یک کاتالوگ سرویس موجود، سریعترین راه برای دستیابی به پوشش اولیه است. این رویکرد یادآور پیادهسازی گیتویهای مرکزی برای مدیریت دسترسی در محیطهای عملیاتی است که نظم ساختاری را به زیرساختها بازمیگرداند.
- در لحظه ایجاد الزام کنید: ویژگیها را اجباری کنید تا موجودی مخازن شما همیشه پاکیزه بماند.
- از گردشکارهای غیرتخریبی استفاده کنید: به جای حذف، از یک مهلت زمانی (Grace Period) و سپس بایگانی استفاده کنید.
- گاردریل (حفاظ) بسازید: فرض کنید منابع داده ممکن است اشتباه باشند. از آستانههای «مرز پایین» و اعلانهای افزونهای (@-mentions) استفاده کنید تا از فجایع ناشی از اتوماسیون جلوگیری کنید.
منتظر تکاملهای بعدی در نحوه ادغام ویژگیهای سفارشی گیتهاب در سیاستهای امنیتی خودکار و تعیین محدوده انطباق باشید.




گفتگو