فهرست مستندات

مدل امنیت#

کجا پیاده شده است#

  • services/registry/src/keys.ts — کلیدهای امضا، و آنچه هرگز نباید از آن‌ها بیرون کشیده شود.
  • services/registry/src/apps/client-assertion.ts و services/registry/src/apps/routes.ts — اینکه اپ چطور ناشرش را ثابت می‌کند و نشستی که می‌گیرد اجازهٔ ادعای چه چیزی دارد.
  • packages/backend/src/app-session.ts — بررسی‌ای که بک‌اند در سوی دیگر همان نشست انجام می‌دهد.
  • services/registry/src/http/route.ts — سطح و سقفی که هر مسیر اعلام می‌کند.
  • services/registry/src/apps/capability-authorization.ts و services/registry/src/apps/consent-page.ts — اختیار قابلیت‌ها، و مراسم رضایتی که فقط مبدأ رجیستری اجازهٔ رندر آن را دارد.

ورود دسکتاپ#

Shell دسکتاپ تنها OAuth client مصرف‌کننده است. کلید دستگاه را enrol می‌کند، نشست شخص را بیرون از renderer هر محصول نگه می‌دارد و درخواست‌های native Runtime را از طرف شخص امضا می‌کند. Environment هیچ redirect URI ثبت نمی‌کند، callback نمی‌گیرد و access، ID یا refresh token دریافت نمی‌کند. فقط projection محدود window.alamr را می‌بیند که main process در اختیارش می‌گذارد.

مقصد بازگشت در سطح محیط هیچ وضعیت مسیریابی محلیِ مبدأیی را حمل نمی‌کند. هاب یک checkpoint اختیاری و یک‌بارمصرفِ ناحیه، وضعیت مکانی و دوربین را فقط در partition خود محصول نگه می‌دارد، آن را با ناوبری فعلیِ متعلق به میزبان اعتبارسنجی می‌کند و پس از بازیابی مصرف می‌کند. مختصات هرگز وارد درخواست‌های رجیستری، توکن‌ها یا محیط مقصد نمی‌شوند و storage محلی غایب یا خراب، بدون تضعیف قواعد بازگشت امن به نقطهٔ آغاز تألیف‌شده برمی‌گردد.

Registry اختیار حساب را نگه می‌دارد؛ Shell یک نشست device-bound دارد و هر Environment نشست Runtime fenced جداگانه‌ای می‌گیرد که فقط از راه bridge نمایش داده می‌شود. هر ورود به حساب نیاز دارد. حالت guest وجود ندارد و هیچ محیطی بازدیدکنندهٔ بی‌نام را نمی‌پذیرد. هاب (environment-zero) دقیقاً همین جریان را دنبال می‌کند.

اینجا یک سیاستِ ورود بود — entry.access با دو مقدار guest_allowed و account_required، به‌همراه حالتِ auto | login | guest — و حذف شد، نه اینکه پیش‌فرض بگیرد. آنچه تصمیم می‌گرفت این بود که رجیستری subjectای با پیشوند guest_ بسازد یا نه؛ یعنی دو جنس subject وجود داشت و هر خواننده‌ای پایین‌دست باید می‌پرسید کدام را در دست دارد. یک جنس، سادهٔ‌شدهٔ دو جنس نیست؛ سیستمِ دیگری است.

خروج از دسکتاپ نشست همان دستگاه را پایان می‌دهد و Runtimeهای محیط و نشست‌های App متصل به آن را باطل می‌کند. این خروج دستگاهی دیگر، نشست جداگانهٔ Developer Console یا CLI یا توکن پروژهٔ CI را باطل نمی‌کند.

نشست‌های اپ#

WebContentsView اپ هیچ توکن دسترسی یا تازه‌سازی محیط، شناسهٔ خام حساب، هویت دستگاه یا Plugin Grant دریافت نمی‌کند. اپ verifier مربوط به PKCE را می‌سازد و فقط challenge و nonce تصادفی bridge را از راه پل تزریق‌شده می‌فرستد. میزبان دسکتاپ پیش از درخواست کد اجرای یک‌بارمصرف، surface، محصول، revision، نسل سند و میزبان فعلی را بررسی می‌کند.

Registry برای مبادله verifier، nonce، کد مصرف‌نشده، revision، نسل میزبان و permission-set digest دقیق را الزام می‌کند. نشست environment-hosted سه‌تایی کامل lease فعال والد را هم می‌خواهد؛ نشست platform-hosted والد ندارد و قابلیت Environment-scoped نمی‌گیرد. پاسخ کوتاه‌عمر، memory-only و App-pairwise است و بک‌اند آن را با Registry introspect می‌کند. scheme و partition ویژهٔ محصول، sandbox الکترون، context isolation، Node integration خاموش، CSP تزریق‌شده و bridge باریک لایه‌های دفاع‌اند، نه sandbox کامل جاوااسکریپت.

هر نشست اپ appAuthMethod دارد. اپ فقط-فرانت‌اند خودش کد launch را مبادله می‌کند و none می‌گیرد: داشتن کد و verifier ناشر را احراز نمی‌کند. اپی که backend اعلام می‌کند confidential client است؛ بک‌اند خودش مبادله را با private_key_jwt و در برابر کلید عمومی و سیاست پذیرش محیطِ ثابت‌شده در manifest منتشرشده امضا می‌کند. introspection روشی را که بک‌اند در acceptedAuthMethods اعلام نکرده باشد رد می‌کند.

private_key_jwt مشارکت ناشر را ثابت می‌کند، نه اینکه شخص همان لحظه در آن بک‌اند عملی انجام داده باشد. پس Notes پیکرهٔ شخصی actor-portable را فقط در اختیار فهرست صریح محیط‌های first-party بازبینی‌شده می‌گذارد، نشست بدون محیط introspectشده را همچنان رد می‌کند و actor یا محیط را از کلاینت نمی‌پذیرد. پیوست اختیاری محیط/مکان provenance تغییرناپذیر است، نه اختیار.

در تنظیم createAppBackend، Notes پیشوند /v2/* را به prefixهای محافظت‌شده و اعتبارسنجی‌شده می‌افزاید (/v1/* همیشه محافظت می‌شود)، پس bearer admission مشترک پیش از handlerهای هر دو نسل API اجرا می‌شود.

بازکردن این پیکره برای محیط دلخواه یا ثالث منتظر مراسم user-presence متعلق به مبدأ Registry می‌ماند؛ backend.environments: "any" جایگزین پذیرفتنی نیست.

هیچ اپی به‌صورت پیش‌فرض context محیط ندارد. درخواست از کاتالوگ بسته و نسخه‌بندی‌شده و به required/optional تقسیم می‌شود. رجیستری assessment و رضایت را به digest دقیق مانیفست، revision، principal policy، scope محیط و authority epoch متصل می‌کند و در launch دوباره می‌سنجد. declaration مانیفست grant نیست.

پل Host Services به نسل متصل است. پیام‌ها schema و اندازهٔ محدود، شناسهٔ یکتا، concurrency و deadline محدود دارند و با پایان نسل cancel می‌شوند. متد ناشناخته، capability یا activation غایب، نسل stale، provider غایب و پاسخ خراب fail closed هستند.

Spatial context معنای پایدار محیط دارد، نه raw account identity یا دسترسی دلخواه به صحنه. capture و assessment navigation عمل موقت کاربر می‌خواهند. Navigation assess/commit تاریخ‌دار و caller-bound است و policy و حرکت در مالکیت محیط باقی می‌ماند. فقط location semantic اعلام‌شده از launch میان محیط‌ها عبور می‌کند. locator captured opaque، bounded و revision-bound است و داخل URL نیست.

تغییر Compact/Workspace فقط presentation میزبان را درجا عوض می‌کند و اختیار تازه‌ای نمی‌سازد. خروج صریح و دیگر خروج‌های مخرب پاسخ lifecycle محدود دارند و نمی‌توانند کاربر را محبوس کنند. در دسکتاپ، جابه‌جایی میان دو Environment سند App را نگه می‌دارد، اما اختیار Runtime قبلی پیش از renewal روی مقصد پایان می‌یابد؛ پاسخ stale یا lease قدیمی نمی‌تواند سند باقی‌مانده را authorize کند.

رسانهٔ نمایشی کاتالوگ#

مسیر داخل باندل برای تصویر در manifest App و Environment ورودی انتشار است، نه URL تحویل. Registry فایل را از archive ازپیش‌اعتبارسنجی‌شده می‌خواند؛ فقط raster محدود PNG و JPEG را می‌پذیرد؛ header و ابعاد را اعتبارسنجی و metadata container را با allowlist بازنویسی می‌کند، بدون ادعای pixel re-encode؛ سپس VersionId شیء خصوصی حاصل را به revision تغییرناپذیر و slot نمایشی دقیق متصل می‌سازد. مسیر عمومی content-addressed فقط تا زمانی بایت می‌دهد که یک binding منتشرشده آن را مجاز کند.

قرارداد پنل و کاتالوگ descriptorهای متعلق به Registry با نام cardIcon و cardCover را منتشر می‌کند. کروم پلتفرم نباید به منبع ناشر fallback کند، چون این کار نشانی شبکه و زمان بازشدن پنل کاربر را از راه URL ناشر افشا و بایت mutable و بازبینی‌نشده را رندر می‌کند. نشان App در حال اجرا همچنان واژگان بسته و ترسیم‌شده به‌دست پلتفرم است و هیچ‌گاه icon کامل کارت App را استفاده نمی‌کند.

اختیار اعلان#

منبع اعلان raw user را آدرس‌دهی نمی‌کند. نشست App باز، principal، host scope و revision دقیق منبع را تعیین می‌کند. Occurrence V1 یک eventId پایدار و محدود به همان منبع، effect بازبینی‌شده و ــ برای Subject کلیددار ــ کلید opaque همراه revision monotonic دامنهٔ منبع می‌دهد. رجیستری همه را با policy event در revision تغییرناپذیر App می‌سنجد. retry دقیق و revision قدیمی no-op پذیرفته‌شده‌اند، پس retry Attention را دوباره باز یا Pulse را replay نمی‌کند.

محتوای source-authored به plain text محدود و action معنایی بسته محدود است. HTML، script، URL دلخواه، raw account subject، artwork mutable منبع و callback رد می‌شوند. template نام‌دار پلتفرم فقط پارامترهای بازبینی‌شدهٔ محدود را می‌گیرد. برای message_waiting، رجیستری actor جفتی اختیاری App را با roster خود گیرنده resolve و title را خودش می‌سازد؛ App title، preview، category یا sound نمی‌فرستد.

workload پس‌زمینهٔ اپ دو credential مستقل دارد: Project token تاریخ‌دار با scope notifications:publish و delegation opaque مخصوص کاربر که از نشست account App ساخته شده است. Registry فقط hash delegation را نگه می‌دارد و در هر signal، مالکیت پروژه، revision/digest فعال، authority epoch ناشر، دو capability اعلان، consent، expiry و revoke را دوباره بررسی می‌کند. Project token تنها user انتخاب نمی‌کند و delegation برای پروژهٔ اپ دیگر بی‌فایده است. پس از پذیرش workload و request متعلق به caller، همهٔ outcomeهای وابسته به recipient فقط 202 {accepted: true} می‌دهند؛ count، treatment، consent، وجود recipient یا read state افشا نمی‌شود.

acknowledgement منبع نیز accepted-only است. App یکی از Subjectهای کلیددار و بازبینی‌شدهٔ خودش و throughRevision را نام می‌برد؛ رجیستری source و recipient را از session می‌گیرد و بیشینهٔ watermark را حتی پیش از وجود Occurrence دیررس ثبت می‌کند. بازشدن App، کلیک روی اعلان یا outcome AppHost acknowledgement نیست؛ محصول فقط پس از نمایش محتوای authoritative تا همان revision watermark را جلو می‌برد.

بک‌اند محیط Environment Backend Grant کوتاه‌عمر با scope notifications.publish@1.0.0، endpoint دقیق و live introspection دارد و App delegation را مصرف نمی‌کند.

دو مسیر هست که در آن‌ها فراخواننده به کسی می‌رسد جز همان شخصی که اعتبارنامه‌اش را در دست دارد، و هر دو هرچه پیدا کنند یکسان جواب می‌دهند.

دومی، محیطی است که به کسانی خبر می‌دهد که به آن اجازه داده‌اند. رضایت روی دامنهٔ خودِ رجیستری داده می‌شود و ردیفی که ثبتش می‌کند شناسهٔ جفتیِ آن شخص در همان محیط را نگه می‌دارد — پس آن نام فقط تا وقتی رضایت زنده است به یک حساب می‌رسد، و پس‌گرفتنِ اجازه آدرس را برمی‌دارد نه اینکه بررسی‌ای اضافه کند. محیط می‌فهمد کسی «بله» گفته، چون مرورگرش از همان‌جا برمی‌گردد، و هرگز خبردار نمی‌شود که پس گرفته است. آنچه می‌فرستد زیر سقفِ ناشر است، پس به صندوق می‌رسد و مزاحمِ هیچ‌کس نمی‌شود مگر خودش بلندش کرده باشد.

یک اعلان می‌تواند برای کسی برخیزد که خودِ فراخواننده نیست. گیرنده تنها با یک aact_ صادرشده زیر کلید همان اپ نام برده می‌شود، و رجیستری آن را به پیوند همراهیِ هم‌اکنون برقرار، اعطای خودِ گیرنده به همان اپ، نبود refusal در هر دو جهت و معلق‌نبودن هیچ‌یک از دو حساب مشروط می‌کند. هر نتیجه یک پاسخ می‌گیرد، پس این مسیر oracle نیست برای اینکه کسی فراخواننده را بلاک کرده، هنوز همراه اوست، یا اصلاً از این اپ استفاده می‌کند. متن را پلتفرم می‌نویسد، نام فرستنده هم در همان متن: نام از فهرست همراهانِ خودِ گیرنده خوانده می‌شود نه از چیزی که اپ فرستاده، و از ADR-0125 این فهرست اصلاً به هیچ محیطی نمی‌رسد — پوستهٔ خودِ کلاینت آن را روی پلنِ دستگاه می‌خواند — پس نام، رویداد را فاش می‌کند نه گراف را. شمار همچنان رد می‌شود، چون حجم پیام‌های فرستنده است و هیچ خواندنی امروز آن را تحویل نمی‌دهد. عنوانِ ساخته‌شده به کرانِ ستون بریده می‌شود، چون این نوشتن تنها پس از عبور از همهٔ بررسی‌های پیش‌نیاز رخ می‌دهد و سرریز دقیقاً در همان حالتی که اعلان می‌نشیند پاسخ دیگری می‌داد.

بلندی مجاز Subject را رجیستری هنگام reduction حل می‌کند و فیلد signal نیست. سقف پیش‌فرض از authorship می‌آید — متن بازبینی‌شدهٔ پلتفرم می‌تواند announce کند؛ متن ناشر حتی برای App first-party پیش‌فرض file دارد — چون سقفی که به category یا family ارسالی caller کلید بخورد self-awarded است. ترجیح گیرنده می‌تواند آن را پایین یا صریحاً بالا ببرد، فقط از مبدأ Registry نوشته می‌شود و treatment به ناشر echo نمی‌شود. mute منبع floor است و «الان نه»ی کل حساب فقط Registry-origin می‌ماند.

Subject حساب زیر principal رجیستری persist می‌شود. Desktop Main با device assertion امضاشده Center کامل حساب را می‌خواند. خواندن Runtime فقط به Environment جاری محدود است؛ Widget هیچ title، body، source، Subject key یا CallAlert کامل حسابی نمی‌گیرد و فقط none | waiting | urgent بدون count می‌بیند.

Presence/control فقط hint بدون محتوا می‌فرستد. client ردیف مجاز و count مستقل کل feed را reconcile می‌کند: unseenCount Subject mute و unseen را هم دارد، attentionCount آن را حذف می‌کند و unreadCount قدیمی alias دومی است. reconnect یا snapshot list Pulse نمی‌سازد. bearer token، delegation secret، raw subject، body و Subject key plaintext وارد log نمی‌شوند.

CallAlert deadline-bound و Delivery per-device planeهای اختیار جدا هستند. نمایش OS یا Shell نمی‌تواند News را seen یا حقیقت تماس را تعیین کند. token و provider push خارجی در V1 وجود ندارند.

مالکیت Runtime#

هر هویت اصلی (principal) در هر لحظه فقط می‌تواند مالک یک زمان اجرای فعال محیط در الامر باشد. این قاعده میان زبانه‌ها و مبدأها و، به کمک آداپتور Redis محیط عملیاتی، میان نمونه‌های رجیستری و دستگاه‌ها نیز برقرار است. صفحهٔ قابل مشاهده‌ای که تازه فعال می‌شود، به‌صورت اتمیک یک اجارهٔ حصاردار (fenced lease) می‌گیرد؛ زمان اجرای قبلی superseded می‌شود، مجوزهای پلاگین آن باطل می‌شوند، حضورش حذف می‌شود و منابع پلاگین‌های سازگار آزاد می‌شوند. بازگشت تمرکز به صفحه، مالکیت را پس نمی‌گیرد؛ کاربر باید صریحاً «فعال‌کردن این صفحه» را انتخاب کند.

حصار فعال‌سازیِ دریافت‌شده تا زمانی در حالت انتظار می‌ماند که رجیستری دوباره اعتبار مجوز را بررسی کند، همان حصار دقیق را به‌صورت پایدار ثبت کند، رویداد ممیزی را بنویسد و پاسخ را آماده سازد. سپس مخزن هماهنگی همان حصار را به‌شکل اتمیک تثبیت می‌کند. جبران یک درخواست ناموفق فقط می‌تواند حصاری را آزاد کند که هنوز تثبیت نشده است؛ بنابراین نمی‌تواند فعال‌سازی هم‌حصاری را که درخواست دیگری با موفقیت تثبیت کرده، بی‌اعتبار کند.

اگر جبران برنده شود، رجیستری تصویر فعال پایدار را فقط زمانی پاک می‌کند که آن رکورد هنوز همان شناسه و نسخهٔ اجارهٔ آزادشده را داشته باشد. خودترمیمی Runtime نیز از همین قاعدهٔ مقایسه‌و‌تنظیم (compare-and-set) پیروی می‌کند تا یک درخواست قدیمی نتواند تصویر جدیدتر همان نشست را غیرفعال کند.

هر درخواست محافظت‌شدهٔ زمان اجرا، شناسهٔ نشست و شناسه و نسخهٔ دقیق اجاره را همراه دارد. حصار قدیمی پاسخ 409 می‌گیرد؛ در دسترس نبودن هماهنگی در محیط عملیاتی پاسخ 503 می‌دهد و درخواست را به‌شکل امن رد می‌کند (fail closed). کیت توسعه هنگام غیرفعال‌بودن، رابط پلاگین از نوع کتابخانهٔ مورد اعتماد را از صفحه خارج می‌کند. این رفتار از APIهای پلتفرم و پلاگین‌های سازگار حفاظت می‌کند، اما برای کد دلخواه نصب‌شده در محیط، sandbox جاوااسکریپت نمی‌سازد.

صفحهٔ غیرفعال یک کانال کنترل فقط‌خواندنی و محدود به نشست و مبدأ را حفظ می‌کند تا خروج سراسری SSO حتی پس از جایگزین‌شدن زمان اجرا فوراً به آن برسد. اعتبارنامهٔ کوتاه‌عمر این کانال هیچ اجارهٔ زمان اجرایی ندارد و سرور همهٔ پیام‌های کارخواه را روی آن رد می‌کند. بنابراین این کانال نمی‌تواند حضور بفرستد، مجوز پلاگین بگیرد، تنظیمات را تغییر دهد یا رابط پلاگین را اجرا کند.

تله‌متری رندرینگ#

تشخیص رندرینگ first-party به‌صورت ساختاری امن برای حریم خصوصی است (ADR-0048). رخدادها به یک فهرست مجاز بسته محدود می‌شوند: نوع رخداد، backend مربوط به renderer، استفاده از fallback، سطح کیفیت، سطل مدت مقداردهی، کد پایدار دلیل پشتیبانی‌نشدن و نتیجهٔ بازیابی. فروشنده و مدل GPU، رشته‌های maskنشدهٔ renderer، VRAM، اطلاعات خام adapter، خطاهای خام درایور و سورسهای shader هرگز جمع‌آوری یا نمایش داده نمی‌شوند؛ صفحهٔ پشتیبانی‌نشده فقط کدهای دلیل پالایش‌شده را نشان می‌دهد. قرارداد زمان اجرا در زمان اجرای رندرینگ ثبت شده است.

اعتماد به پلاگین#

زمان اجرای اولیهٔ فرانت‌اند، کتابخانه‌ای مورد اعتماد (trusted library) است که هنگام build نصب می‌شود. مجوزهای اعلام‌شده سه دسته‌اند:

  • مجوزهای پلتفرم که الامر می‌تواند اعمال کند؛
  • نیازمندی‌های مرورگر و افشای دسترسی شبکه و داده؛
  • دامنه‌های مجوز بک‌اند.

آرتیفکت‌های مدیریت‌شده تغییرناپذیر و نشانی‌پذیر بر پایهٔ محتوا هستند. اعتبارسنجی، مواردی مانند پیمایش مسیر، پیوند ناامن، بمب tar، فایل اجرایی بومی، اسکریپت چرخهٔ عمر، اطلاعات محرمانه و ناسازگاری یکپارچگی را رد می‌کند.

کش آرتیفکت منتشرشدهٔ Registry یک نسخهٔ تأییدشده برای سروکردن است، نه منبع اختیار. پذیرش در کش و خواندن از آن، SHA-512 مشتق‌شده از خود بایت‌ها را در برابر VersionId دقیق Object Storage می‌سنجد. Registry وضعیت انتشار و مسدودسازی اضطراری را هم پیش و هم پس از بازکردن بایت‌های کش دوباره بررسی می‌کند؛ بنابراین اگر مسدودسازی هنگام خواندن از storage ثبت شود، پیش از آغاز پاسخ برنده است. پوشش ناقص یا خراب، readiness را به‌شکل امن می‌بندد و مسیرهای نوشتن و draftهای کش‌نشده هرگز از کش استفاده نمی‌کنند.

مجوزهای بک‌اند#

رجیستری، محیط، کاربر، مبدأ و نشست را از توکن حامل زمان اجرا به دست می‌آورد. مجوز پلاگین (Plugin Grant) فقط برای انتشار دقیق نصب‌شده و استقرار بک‌اند منتشرشده صادر می‌شود. سرویس، نوع، مخاطب، دامنه، بافتار امضاشده، مبدأ، زمان انقضا و سیاست بازپخش را با JWKS الامر بررسی می‌کند. این مجوز همچنین مقدار anonymous مشتق‌شده در رجیستری و environmentConfig عمومیِ نصب تغییرناپذیر را همراه دارد؛ در نتیجه بک‌اند می‌تواند سیاست‌هایی مانند دسترسی مهمان در Media را بدون اعتماد به ورودی مرورگر اعمال کند.

مجوز، حصار اجارهٔ زمان اجرا را نیز همراه دارد. یک اتصال بلندمدت بک‌اند باید وضعیت این حصار را درون‌نگری کند و در برابر هر حکم قطعی — باطل‌شده، جایگزین‌شده، منقضی یا ناهم‌خوانی اجاره — اتصال را به‌شکل امن رد کند. اگر هماهنگی رجیستری صرفاً در دسترس نباشد، اتصال می‌تواند مجوز پیش‌تر تأییدشدهٔ خود را فقط در یک پنجرهٔ تأیید محدود نگه دارد: حداکثر سه درون‌نگری ناموفق پیاپی و هرگز فراتر از exp همان مجوز؛ پس از آن باز هم امن رد می‌شود، ولی با دلیلِ بستنِ متمایز و قابل‌تلاش‌مجدد تا کلاینت بتواند اختلال هماهنگی را از ابطال تشخیص دهد. بدون این کران، یک استقرار چندثانیه‌ای رجیستری به قطع سراسری ناوگان و هجوم فوری صدور مجدد روی همان صادرکنندهٔ در حال بازیابی تبدیل می‌شود. Media و Multiplayer این بررسی را هنگام پیوستن و سپس به‌طور دوره‌ای تا پایان اتصال انجام می‌دهند. دریافت JWKS و درون‌نگری به مبدأ دقیق صادرکنندهٔ رجیستری و مسیرهای مرجع محدود است، تغییر مسیر را رد می‌کند و برای اندازهٔ پاسخ سقف می‌گذارد؛ فرایند کشف نمی‌تواند مقصدی دلخواه برای درخواست حامل توکن انتخاب کند.

Media v2 برای پیوستن، ازسرگیری و تمدید روی همان socket یک Grant تازه و یک‌بارمصرف مصرف می‌کند و شناسه و نسل اتصال، نسخهٔ lease زمان اجرا و انقضا را با exact-CAS در Redis حصار می‌کشد. تمدید، عضویت موجود را حفظ می‌کند و نمی‌تواند پیوستن را بازپخش کند. شروع stage و ارتقای سخنران، scope جاری media:publish و callback اختیار نقشِ Environment را دوباره بررسی می‌کنند؛ TrackSIDهای ادعاشدهٔ میکروفن و اشتراک صفحه نیز پیش از فعال‌شدن انتشار با LiveKit تطبیق داده می‌شوند.

اختیار تماس مستقیم، شروع تماس را از پاسخ‌دادن جدا می‌کند. مهمان هرگز media:direct-call نمی‌گیرد و نمی‌تواند تماس را آغاز کند، اما سیاست callee_only می‌تواند actorKey محدود به scope همان مهمان را به تماس‌گیرندهٔ احراز‌شده در همان scope اعلام کند. سرویس Media ــ نه مرورگر ــ دسترس‌پذیری جاری تماس را منتشر می‌کند و هر دو طرف را تا رسیدن دعوت به وضعیت نهایی رزرو نگه می‌دارد.

اعتبارنامه‌های LiveKit کوتاه‌عمر و حداقل‌اختیار هستند، اما transport برقرارشدهٔ WebRTC را فوراً باطل نمی‌کنند. بنابراین ابطال از راه state رسانه، فراخوانی‌های مجوز/حذف ادمین LiveKit، webhookهای امضاشده و صف Projection مستقل، lease-based و مبتنی بر desired state همگرا می‌شود. صف هیچ credentialای ندارد، روی تازه‌ترین نسخهٔ نشست coalesce می‌شود و فقط lease دقیق worker همان نسخه می‌تواند آن را کامل کند. استقرار خودمیزبان ادعای ابطال آنی توکن ندارد. Media محتوا را ضبط یا رونویسی نمی‌کند و کلیدهای E2EE تماس مستقیم و جلسه فقط در state نشست فعال و تحویل به مرورگر مجاز وجود دارند. گذار terminal پیش از نگهداری محدود، E2EE را حذف می‌کند.

webhookهای LiveKit فقط از /internal/livekit/webhook وارد می‌شوند. Media payload خام امضاشده و claim مربوط به SHA-256 را بررسی می‌کند، application/webhook+json را الزامی می‌داند، payload بزرگ‌تر از ۲۵۶ KiB را رد می‌کند و UUID محدود رویداد را با ledger پردازش claim/complete/release در Redis رفع تکرار می‌کند. handler ناموفق claim را آزاد می‌کند تا retry امضاشده پردازش شود و رویداد کامل‌شده deduplicated می‌ماند. webhook احرازشده صرفاً مشاهده است، نه مجوز؛ همهٔ اصلاح‌ها همچنان از state مرجع Media و تطبیق دقیق participant، TrackSID، source، holder و generation مشتق می‌شوند.

endpointهای پرتکرار Runtime پیش از احراز هویت یک سقف کلی برای سوءاستفادهٔ شبکه نگه می‌دارند و سپس محدودیت عملیاتی خود را بر نشست تأییدشدهٔ Runtime اعمال می‌کنند. بنابراین صدور Grant و heartbeat حضور برای کاربران معتبر پشت یک پراکسی مستقل می‌ماند و یک نشست نیز نمی‌تواند با چرخاندن headerها بودجهٔ خودش را دور بزند.

Multiplayer از همین مرز Grant پیروی می‌کند. بک‌اند آن کنشگر، محیط، مبدأ، انتشار، مولفه و scope را از claimهای اعتبارسنجی‌شده می‌گیرد، نه از payload امضانشدهٔ پیوستن. JSON فقط برای کنترل است و طول state دودویی در برابر layout تغییرناپذیر بررسی می‌شود. transformهای متناهی، sequence پیمانه‌ای، اندازهٔ پیام، آهنگ ارسال، epoch اتاق، ظرفیت، محدودهٔ حرکت و تعداد discontinuity اعتبارسنجی می‌شوند. یک transform اجتماعی خارج از این محدوده حذف می‌شود، اما آخرین pose پذیرفته‌شده، socket، حصار کنشگر و عضویت اتاق دست‌نخورده می‌مانند؛ transform نامعتبر هرگز به‌معنای خروج از اتاق نیست. تلرانس burst محدود، کنترلر رسمی Player Rig را در cadence انتشار ۲۰ هرتز، از جمله ورودی بیشینهٔ چرخش، پوشش می‌دهد ولی flip آنی quaternion را نمی‌پذیرد. Redis در محیط عملیاتی بازپخش Grant، نشست‌های هم‌زمان یک کنشگر و مالکیت تک‌نویسندهٔ اتاق را به‌صورت اتمیک حصار می‌کشد. کلیدهای اتاق و کنشگر مقادیر SHA-256 هستند؛ subjectهای خام و برچسب scope وارد کلید، log یا label متریک نمی‌شوند. Resume پس از قطع ناگهانی هم به Grant تازه و هم ticket پرآنتروپیِ چرخان و مقید به همان کنشگر و اتاق نیاز دارد. pose حرکت فقط دادهٔ نمایشی است و اثباتی برای collision معتبر یا بازی عاری از تقلب نیست. اگر WebSocket جایگزین با transport قبلی هم‌پوشانی داشته باشد، همین مدرک عضویت نگه‌داری‌شده را به‌صورت اتمیک به اتصال جدید مقید می‌کند، ticket را می‌چرخاند و transport قدیمی را می‌بندد. suspend به شناسهٔ اتصال مقید است؛ بنابراین close دیرهنگام socket قدیمی نمی‌تواند جایگزین خود را suspend یا حذف کند. تمدید عادی authorization با یک Grant تازه و یک‌بارمصرف روی همان socket انجام می‌شود. Environment، انتشار و مولفهٔ Plugin، actor و actorKey، origin، scope، نشست و lease Runtime، پیکربندی Environment و permissionهای بازبینی‌شدهٔ امضاشده باید دقیقاً یکسان باشند. Redis پیش از تعویض timerهای محلیِ generation-scoped، شناسهٔ اتصال و نسخهٔ lease موجود را exact-CAS می‌کند؛ بنابراین timer قدیمی نمی‌تواند authorization تمدیدشده را ببندد. تغییر lease Runtime همیشه reconnect است و هرگز میان‌بری برای renewal نیست.

ارسال به مالک اتاق روی HTTP/2 خصوصی با mTLS انجام می‌شود. رکورد حصارشدهٔ Redis فقط مبدأ دقیق مالک را می‌دهد؛ هر همتا باید گواهی صادرشده از CA خوشه ارائه کند، redirect وجود ندارد، اندازهٔ frame محدود است و خرابی مسیر هرگز به ساخت نویسندهٔ محلی رقیب منجر نمی‌شود. هر نود برای هر مالک فقط یک session تجمیع‌شده نگه می‌دارد تا تعداد مرورگرها به تعداد connectionهای داخلی تبدیل نشود.

scope در Multiplayer یک مرز جداسازی است، نه کنترل دسترسی. محیطی که Zone محدود دارد در نصب تغییرناپذیر خود پذیرش environment_backend را فعال می‌کند. Registry پیش از صدور Plugin Grant، یک EnvironmentBackendGrant سی‌ثانیه‌ای را فقط با هویت Runtime، نقش‌های Environment و scope درخواستی به endpoint ثبت‌شده در همان revision می‌فرستد. تغییر مسیر رد می‌شود، فراخوانی پس از ۵۰۰ میلی‌ثانیه timeout می‌شود و پاسخ بزرگ‌تر از ۱۶KiB پذیرفته نیست. timeout، پاسخ نامعتبر و deny صریح همگی بدون افشای occupancy با خطای عمومی scope_denied به‌شکل fail-closed رد می‌شوند.

پروفایل نمایشی peer از claimهای Grant اعتبارسنجی‌شده در سرور مهر می‌خورد و به‌صورت متادیتای کم‌نرخ عضویت فرستاده می‌شود؛ پیام pose هرگز آن را تعیین نمی‌کند. همبستگی آواتار میان Pluginها فقط با actorKey محدود به Environment امضاشده و scope بافتار انجام می‌شود. Registry کلید نهایی را از principal جفتی Environment که با HMAC محافظت شده است می‌سازد و همان claim را در Grant همهٔ Pluginهای همکار امضا می‌کند. BackendComponent نباید همبستگی را از actorSub متفاوت و جفتی هر Plugin مشتق کند. کد مرورگر subject خام حساب را دریافت نمی‌کند و کلیدهای Zone یا Environment متفاوت یک هویت واحد محسوب نمی‌شوند.

لایهٔ کنترل#

پرتال توسعه‌دهندگان (Developer Portal) با همان فراهم‌کنندهٔ OIDC احراز هویت می‌کند و توکن مدیریت اعتبارسنجی‌شده را با یک نشست مبهم، مرتبط و ذخیره‌شده در پایگاه‌داده عوض می‌کند. کنسول از cookie امن با ویژگی‌های HttpOnly و Strict، حفاظت CSRF، گزارش ممیزی و محدودسازی نرخ استفاده می‌کند. رابط خط فرمان و CI توکن‌های مستقل، محدود و دارای انقضا دارند.

mutation جای‌گذاری Hub اختیار مدیریت پلتفرم است، نه اختیار Project یک Environment. Registry فقط set/replace/clear مدیر احراز‌شده را می‌پذیرد، توکن Project را رد می‌کند، اسلات تألیف‌شده و شرایط کاتالوگ عمومی را می‌سنجد، انتخاب خود Hub به‌عنوان مقصد را ممنوع می‌کند و برای هر مقصد در Hub فقط یک تخصیص می‌پذیرد. هر mutation دلیل محدود می‌خواهد. نوشتن assignment و رویداد ممیزی hub.placement.set یا hub.placement.clear در یک تراکنش commit می‌شود؛ شکست audit جای‌گذاری را rollback می‌کند. اسلات fixed از این API تغییرناپذیر است.

Hub فقط projection عمومی جای‌گذاری را مصرف می‌کند. این projection شناسهٔ مقصد را فقط برای اسلات واجد شرایط occupied افشا می‌کند. نبود حقیقت Registry/catalog و تخصیص فاقد شرایط به لندمارک غیرقابل ورود reserved تبدیل می‌شود و client نمی‌تواند مقصد checked-in و stale را بازیابی کند.

ورود مرورگری alamr login یک مجوزدهی برای کارخواه بومی و عمومی است. حتی وقتی مرورگر نشست SSO و Grant قبلی دارد، رجیستری صفحهٔ consent بدون cache و غیرقابل‌قرارگیری در frame را نشان می‌دهد که نام Al-Amr CLI و scopeهای دقیق درخواست‌شده را نمایش می‌دهد. جریان فقط پس از POST صریح، هم‌مبدأ و متصل به CSRF ادامه می‌یابد و رد کاربر خطای OAuth با کد access_denied برمی‌گرداند. این تأیید برای هر مجوزدهی مرورگری لازم است، زیرا client_id عمومی و redirect از نوع loopback هویت پردازهٔ محلی آغازکننده را اثبات نمی‌کنند. PKCE کد حاصل را به همان پردازه متصل می‌کند. CLI شنوندهٔ موقت 127.0.0.1 را بلافاصله پس از دریافت پاسخ مجوز و پیش از تبادل توکن می‌بندد.

پشتیبانی loopback دوپشته یک پیگیری بعدی است: افزودن [::1] باید یک URI پایهٔ loopback دقیق و دوم ثبت کند و استثنای تغییر redirect را همچنان فقط برای CLI و فقط برای پورت نگه دارد. callback مرورگری فعلی عمداً روی IPv4 127.0.0.1 گوش می‌دهد.

بررسی‌های سلامت، مقصدهای خصوصی و link-local را رد می‌کنند، تغییر مسیر و DNS را دوباره می‌سنجند و زمان و اندازهٔ پاسخ را محدود نگه می‌دارند.

preview محلی و نقش مالک

پیش از اولین انتشار و فقط بیرون production، جدیدترین draft زمانی می‌تواند Runtime client موقت باشد که محیط active revision نداشته باشد و entry و همهٔ callbackها loopback باشند. production و هر draft غیرمحلی همچنان به انتشار معتبر نیاز دارند.

رجیستری فقط وقتی environmentRoles: ["admin"] صادر می‌کند که کاربر احراز‌شده مالک Project همان Environment باشد. مهمان و غیرمالک نقشی نمی‌گیرند و claim شامل شناسهٔ خام مالک نیست. HTTP loopback نیز فقط بیرون production برای handshake محلی پذیرفته می‌شود؛ production همچنان HTTPS می‌خواهد و loopback را رد می‌کند. انتقال اتمیک مالکیت، مشتق‌سازی گرنت‌های جدید را فوراً به مالک جدید می‌سپارد؛ گرنت کوتاه‌عمر قبلی همچنان با انقضا، lease، لغو و introspection زنده محدود می‌شود و هیچ نقش ادعاشده‌ای از کلاینت پذیرفته نمی‌شود.

تعریف‌های رول مدیریت‌شدهٔ پلتفرم این مرز را گسترش می‌دهند (ADR-0055). مدیران رجیستری رول‌هایی با اسکوپ همهٔ Environmentها یا یک مجموعهٔ مشخص از آن‌ها تعریف و به حساب‌ها تخصیص می‌دهند. هنگام صدور یا بازمحاسبهٔ session، رجیستری کلیدهای رولِ در-اسکوپِ کاربر را resolve و پس از کلید مشتق‌شدهٔ "admin" ادغام می‌کند و یک environmentRoleCatalog با همهٔ رول‌های موجود در آن Environment ضمیمه می‌سازد. تعریف‌ها و تخصیص‌ها فقط در وضعیت رجیستری زندگی می‌کنند، فقط از طریق API مدیریتی ممیزی‌شدهٔ /v1/admin/* تغییر می‌کنند، و رول‌های بایگانی‌شده بلافاصله برای sessionهای تازه resolve نمی‌شوند.

امنیت بک‌اند Environment

بک‌اند Environment توکن Runtime مرورگر یا نقش ادعاشده در JSON را نمی‌پذیرد. SDK یک EnvironmentBackendGrant کوتاه‌عمر می‌گیرد که Registry آن را به audience، endpoint، scope، context، origin و fence همان Runtime محدود کرده است. بک‌اند JWKS رجیستری را بررسی و به‌طور پیش‌فرض introspection زنده انجام می‌دهد تا logout، انقضا، لغو یا supersede شدن فوراً اثر کند. نقش‌ها و داده‌های اختصاصی Environment در همان سرویس ذخیره می‌شوند؛ Registry فقط مرز هویت پلتفرم، ادمین اولیه و لغو مجوز را نگه می‌دارد.

اختیار استقرار staging#

اختیار استقرار GitLab از کلید اختصاصی SSH حساب al-amr-ci و host keyهای pin‌شده تشکیل می‌شود. این اختیار به گذرواژهٔ مالک، توکن bootstrap ادمین، URL پایگاه‌داده یا credential ذخیره‌ساز S3 دسترسی ندارد. این secretها فقط به‌صورت root-owned روی همان میزبانی می‌مانند که به آن‌ها نیاز دارد. حساب بدون امتیاز build فقط پیکربندی عمومی build را می‌گیرد؛ upload به Object Storage با helper ثابت root و credential نویسندهٔ محلی host انجام می‌شود و roleهای دیگر credential محلی فقط‌خواندنی دارند. onboarding یک‌باره، توکن‌های automation محدود به پروژه و دارای انقضا را در فایل root-only Runtime می‌چرخاند؛ مقدار خام توکن هرگز وارد CI، receipt کاتالوگ یا log نمی‌شود.

اجرای root به subcommandهای بازبینی‌شدهٔ al-amr-deploy محدود است. استخراج archive مسیر مطلق یا traversal، ورودی device/FIFO/hardlink و symlink فراری را رد می‌کند. deploy عادی نمی‌تواند Caddy، systemd، hosts، firewall، userها یا secretها را تغییر دهد و ناسازگاری opsRevision به‌صورت fail-closed متوقف می‌شود. journalها و RECOVERY_REQUIRED نمی‌گذارند pipeline بعدی شواهد mutation ناتمام را بازنویسی کند.

پیکربندی میزبان و نصب secret عمداً خارج از GitLab انجام می‌شود: هویت آفلاین al-amr-ops یک opsRevision منطبق را از پیکربندی محلی ignoreشده اعمال می‌کند. اختیار bootstrap کاربر ubuntu پس از پذیرش staging حذف می‌شود و کلید CI نمی‌تواند فرمان دلخواه را با دسترسی root اجرا کند.

انتشار staging هویت‌های مالک، reviewer و recovery را جدا نگه می‌دارد. deploy عادی مالکیت را بدون adoption یا transfer بررسی می‌کند. بایت Plugin فقط از مرز artifact-validator می‌گذرد و VersionId دقیق Object Storage به‌همراه SHA-512 ثبت می‌شود؛ metadata رجیستری به‌تنهایی مدرک integrity نیست. snapshot وضعیت ادمین sanitized، فقط‌خواندنی و دارای حد تازگی است و هیچ action، credential یا dump متغیر محیطی ندارد.

کتابخانه‌های مورداعتماد و integrity مربوط به artifact#

بسته‌های مرورگر در v0.x کد کتابخانهٔ مورداعتماد هستند. مجوزهای اعلام‌شدهٔ Plugin یک سطح افشا و بازبینی‌اند، نه یک sandbox جاوااسکریپتی، و نه مستندات و نه عامل‌ها مجازند آن‌ها را sandbox توصیف کنند. بازبینان مجوزهای اعلام‌شده، originهای شبکه و افشای داده را به‌عنوان evidence ارزیابی می‌کنند؛ پلتفرم ادعای محصورسازی runtime ندارد.

بایت‌های بستهٔ مدیریت‌شده فقط از مرز artifact-validator می‌گذرند. metadata مربوط به Registry — سطرهای کاتالوگ، مانیفست‌ها، رکوردهای نسخه — به‌تنهایی هرگز مدرک integrity یا ایمنی نیست. دانلودها digest و اندازهٔ دقیق را دوباره اعتبارسنجی می‌کنند و بازرسی artifact توسط بازبین از طریق BFF هم‌مبدأ پرتال با بازاعتبارسنجی digest و اندازه جریان می‌یابد، به‌جای اعتماد به یک URL ذخیره‌شده.

ایزولاسیون بازبینی و محتوای untrusted#

Runtimeهای خارجی untrusted هستند. بازرسی بازبینی هرگز credential یا referrer پرتال را به URL یک Runtime خارجی متصل نمی‌کند و به پروفایل مرورگر ایزوله نیاز دارد. پذیرش عمومی Runtimeهای ناشران ناشناس تا زمانی که مرورگر یا پروکسی بازبینی راه‌دور با egress filtering، DNS pinning، بازاعتبارسنجی redirect و مسدودسازی endpointهای metadata و داخلی وجود نداشته باشد، غیرفعال می‌ماند؛ baseline مربوط به رد URL به‌تنهایی برای این کار کافی نیست.

متن ناشر، توضیحات کاتالوگ، مانیفست‌ها و محتوای artifact دادهٔ untrusted هستند. آن‌ها هرگز دستورالعمل برای بازبینان یا عامل‌های کدنویسی نیستند و هرگز بر منابع digest-bound، مانیفست‌های تغییرناپذیر یا وضعیت Registry غلبه نمی‌کنند. ابزارهای بازبینی و سطوح عامل‌محور باید دستورالعمل‌های تعبیه‌شده در چنین محتوایی را تلاش برای prompt injection تلقی کنند، نه راهنمای پلتفرم.