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

مرجع API#

یک API عمومی در /v1 به Developer Console، CLI، Hub و Environmentهای مستقل خدمت می‌دهد. هیچ workflow مدیریتی فقط در Console گرافیکی وجود ندارد.

سطح‌های API#

  • Catalog ناشناس است و Environment، App، Plugin، ناشر، revision، نسخه، سازگاری، اعتماد و سلامت منتشرشده را ارائه می‌کند.
  • Management برای پروژه، عضویت، نشست CLI، draft، artifact، deployment، token، rollback و publication از نشست امن Console یا credential محدود CLI/CI استفاده می‌کند.
  • Runtime نشست standby محدود به origin می‌سازد، برای هر principal یک lease حصارشده را اتمیک فعال می‌کند و همان session/lease fence را برای Presence، settings، App launch، اعلان و Plugin Grant الزامی می‌داند. BackendComponent می‌تواند وضعیت زندهٔ lease یک Grant را introspect کند.
  • Device با device assertion امضاشده به Desktop Shell ثبت‌شده اجازه می‌دهد دادهٔ کامل حساب، مانند News Center، را بخواند. bearer متعلق به App یا Environment به این plane راه ندارد.
  • OIDC برای clientهای عمومی ثبت‌شدهٔ Environment و CLI از Authorization Code همراه PKCE استفاده می‌کند.

کشف عمومی App از GET /v1/catalog/apps و GET /v1/catalog/apps/{publisher}/{slug} استفاده می‌کند. پاسخ جزئیات، کارت Catalog را به AppRevision فعال و منتشرشدهٔ دقیق متصل می‌کند؛ این خواندن ناشناس App را به Runtime اختصاص نمی‌دهد و اختیار launch صادر نمی‌کند.

endpointهای چرخهٔ Runtime عبارت‌اند از POST /v1/runtime/session، GET /v1/runtime/session، POST /v1/runtime/session/activate و POST /v1/runtime/session/release. درخواست مرورگر همیشه Origin، درخواست وابسته به نشست X-Al-Amr-Runtime-Session و درخواست محافظت‌شدهٔ فعال علاوه بر آن X-Al-Amr-Runtime-Lease و X-Al-Amr-Runtime-Lease-Version را می‌فرستد. OpenAPI این الزام را برای هر operation اعلام می‌کند.

POST /v1/runtime/control-token تنها endpoint خواندنی است که عمداً برای نشست معتبر standby یا superseded باز می‌ماند. credential کوتاه‌عمر WebSocket آن محدود به origin، فقط‌خواندنی و بدون lease claim است و فقط رویداد activity و خروج سراسری مرورگر را دریافت می‌کند. کانال کنترل همهٔ پیام‌های client را رد می‌کند و برای Presence یا APIهای Plugin قابل استفاده نیست.

POST /v1/runtime/session/activate هم نشست superseded را می‌پذیرد، و باید بپذیرد: پس‌گرفتن Runtime بعد از اینکه تبی دیگر آن را برده، دقیقاً همان کاری است که نشست superseded برای آن وجود دارد. آنچه محدودش می‌کند سهمیه است نه رد کردن — یک حساب می‌تواند Runtime زندهٔ خودش را در هر دقیقه شمار محدودی بار بیرون کند، و سهمیه به اصلِ خصوصی بسته است، چون منبعِ مورد رقابت یک ردیف در runtime_leases به ازای هر حساب است، نه به ازای هر نشست یا هر محیط. عبور از آن 429 می‌دهد، پیش از آنکه انبارهٔ هماهنگی دست بخورد.

خطاهای اصلی authorization، سیاست حساب، fencing و coordination زمان اجرا از قرارداد تخت application/problem+json سازگار با RFC 7807 استفاده می‌کنند. کدهای پایدار آن runtime_token_invalid، runtime_authorization_expired، account_required، runtime_session_standby، runtime_session_superseded، runtime_lease_stale و runtime_coordination_unavailable هستند. Runtime قدیمی یا superseded پاسخ 409 و قطعی coordination پاسخ 503 می‌گیرد؛ هیچ‌کدام کسی را بی‌نام راه نمی‌دهند. خطاهای عمومی validation و Catalog/Management همچنان قرارداد تو‌در‌توی ApiError با application/json دارند.

ورود به Environment نه حالتی دارد و نه سیاستی: هر authorization به حساب نیاز دارد، پس entry.access و پارامترِ auto | login | guest هر دو حذف شدند نه پیش‌فرض. خروج SSO مرورگر نشست‌های Runtime و Console مرتبط با همان مرورگر را revoke می‌کند؛ credentialهای CLI و CI مستقل‌اند.

پاسخ Runtime session فیلدهای environmentRoles و environmentRoleCatalog دارد. environmentRoles کلید مشتق‌شدهٔ "admin" را وقتی کاربر مالک Environment Project است (فقط در همان Environment، نه subject سراسری حساب) و پس از آن کلیدهای رول مدیریت‌شدهٔ پلتفرمی را دارد که به کاربر تخصیص یافته و اسکوپشان سراسری است یا آن Environment را شامل می‌شود. environmentRoleCatalog همهٔ رول‌های موجود در Environment را فهرست می‌کند — رول مشتق‌شدهٔ admin به‌علاوهٔ هر تعریف رول بایگانی‌نشدهٔ قابل‌مشاهده در آنجا — با scope: "all" | "scoped". حاکمیت تعریف و تخصیص رول در ADR-0055 آمده است.

ProjectView احرازشده خلاصهٔ خصوصی owner و currentRole درخواست‌کننده با یکی از مقادیر owner | admin | developer | viewer را دارد. مالک Environment Project نقش Runtime admin حاصل را نیز می‌بیند. Project admin نمایندهٔ مدیریت است و خودکار Runtime admin نمی‌شود. Catalog ناشناس به‌جای مالک شخصی Publisher را نشان می‌دهد.

عضویت پروژه#

عضویت از GET و POST /v1/projects/{projectId}/members، PUT /v1/projects/{projectId}/members/{memberId} و POST /v1/projects/{projectId}/members/{memberId}/remove استفاده می‌کند. انتقال مالکیت با POST /v1/projects/{projectId}/ownership/transfer، شناسهٔ عضو غیرمالک موجود، نقش باقی‌ماندهٔ مالک قبلی (admin | developer | viewer) و دلیل auditشده انجام می‌شود. پاسخ فقط شناسهٔ عضو Project، نام نمایشی، نقش، ارتباط با کاربر جاری و زمان‌ها را برمی‌گرداند و subject خام یا email قابل استفادهٔ مجدد را افشا نمی‌کند.

مالک همهٔ عملیات Project را انجام می‌دهد. admin فقط developer و viewer را مدیریت می‌کند و نمی‌تواند اختیار owner/admin بدهد یا حذف کند. developer draft و publication مجاز را پیش می‌برد اما member یا credential را مدیریت نمی‌کند؛ viewer فقط‌خواندنی است. هر تغییر عضویت دلیل و audit record می‌خواهد. Project token نمی‌تواند عضویت را تغییر دهد یا token دیگری بسازد/revoke کند. فقط مالک معتبر جاری می‌تواند مالکیت را منتقل کند. Registry کاهش نقش مالک قبلی، ارتقای هدف، تغییر owner_user_id و ثبت project_owner.transfer را در یک transaction انجام می‌دهد. Grantهای بعدی Environment نقش admin را از مالک تازه می‌گیرند؛ Grantهای کوتاه‌عمر قبلی همچنان به expiry و introspection خود محدودند.

نشست‌های مدیریت CLI#

GET /v1/auth/management-sessions Grantهای OAuth فعال CLI کاربر را فهرست و DELETE /v1/auth/management-sessions/{sessionId} یک نشست دقیق متعلق به همان کاربر را همراه access/refresh material آن revoke می‌کند. caller مبتنی بر cookie به CSRF نیاز دارد. پاسخ هرگز access token، refresh token، authorization code یا subject خام را برنمی‌گرداند. نشست SSO مرورگر و Project token کلاس‌های جدا هستند.

مدیریت پلتفرم#

مدیران رجیستری دایرکتوری حساب‌ها و رول‌های مدیریت‌شدهٔ پلتفرم را از طریق GET /v1/admin/users (جست‌وجو و صفحه‌بندی با cursor) و GET /v1/admin/users/{userId} (نمایه، تخصیص‌های رول، پروژه‌ها) و همچنین GET|POST /v1/admin/roles، GET|PATCH /v1/admin/roles/{roleId}، POST /v1/admin/roles/{roleId}/archive، GET /v1/admin/roles/{roleId}/assignments و PUT|DELETE /v1/admin/roles/{roleId}/assignments/{userId} مدیریت می‌کنند. هر مسیر به هویت Console یا مدیریتی با ادمین پلتفرم نیاز دارد؛ Project token با admin_required رد می‌شود.

PUT /v1/admin/users/{userId}/platform-admin با بدنهٔ { reason } دسترسی مدیر پلتفرم را به یک حساب موجود می‌دهد. فقط مدیر فعلی با نشست Console محافظت‌شده با CSRF مجاز است و Project token رد می‌شود. تغییر کاربر و رویداد ممیزی platform.admin.grant به‌صورت اتمیک ثبت می‌شوند و تکرار درخواست برای مدیر موجود بدون اثر جانبی است.

کلیدهای رول kebab-case و تغییرناپذیرند و کلید مشتق‌شدهٔ admin مخصوص مالک Environment رزرو است. اسکوپ صریح است: scopeMode: "all" یعنی همهٔ Environmentها — از جمله آن‌هایی که بعداً ساخته می‌شوند — و scopeMode: "selected" فقط همان Environmentهای فهرست‌شده؛ فهرست خالی هرگز global پنهان نیست. تخصیص می‌تواند زیرمجموعه‌ای از اسکوپ رول را با environmentIds بگیرد (در رول سراسری هر زیرمجموعه‌ای معتبر است) و زیرمجموعهٔ خالی کل اسکوپ رول را پوشش می‌دهد. mutationها محدودیت‌های Runtime را اعمال می‌کنند (۶۳ تعریف فعال، ۱۶ تخصیص به‌ازای حساب) با role_quota_exceeded و role_assignment_quota_exceeded، ویرایش رول با expectedUpdatedAt از نوشتن هم‌زمان جلوگیری می‌کند (role_version_conflict)، و هر mutation به‌صورت اتمیک با رکورد ممیزی‌اش ثبت می‌شود.

اختیار reviewer عمداً از رول‌های Runtime و platform admin جداست. GET /v1/admin/reviewer-authorities اختیارهای فعال تفویض‌شده را فهرست می‌کند. PUT /v1/admin/reviewer-authorities/{userId} و DELETE روی همان مسیر بدنهٔ { reason } می‌گیرند؛ فقط مدیر پلتفرم مجاز است، توکن Project رد می‌شود و عامل، هدف، دلیل و تاریخچهٔ اعطا/لغو به‌صورت اتمیک همراه رویداد ممیزی ثبت می‌شوند. مدیران به صف دسترسی دارند، اما اجرای میزبان بازبینی حتی برای مدیر به اختیار reviewer صریح و قابل‌لغو نیاز دارد. مدیر می‌تواند این اختیار را صریحاً به خودش اعطا کند. پذیرش مدیریت Desktop به scope سروری reviewer:admin نیاز دارد. بازبین تفویض‌شدهٔ فعال می‌تواند صف، شواهد و تصمیم بازبینی را استفاده کند اما نمی‌تواند کاربران را مدیریت کند، به Projectها دسترسی بگیرد، منتشر کند یا جای‌گذاری Hub انجام دهد.

اپ مستقل «پنل مدیریت» (platform/admin-panel با کلاینت OIDC به‌نام al-amr-admin-panel) سطح گرافیکی بازبینی‌شدهٔ این گردش‌کارهاست؛ این گردش‌کارها از طریق alamr admin users و alamr admin roles نیز در دسترس‌اند و هرگز فقط-کنسولی نیستند.

جای‌گذاری‌های Hub#

GET /v1/hubs/{hubEnvironmentId}/zones/{zoneId}/placements مدل خواندن ناشناس Hub است. شناسه، برچسب، حالت گزینش، ترتیب و وضعیت مشتق‌شدهٔ open | occupied | reserved اسلات تألیف‌شده را برمی‌گرداند. فقط برای اسلات واجد شرایط occupied مقدار destinationEnvironmentId را می‌فرستد.

GET /v1/admin/hubs/{hubEnvironmentId}/zones/{zoneId}/placements به مدیر پلتفرم نیاز دارد و برای عیب‌یابی assignment ذخیره‌شده، actor تغییردهنده و زمان تغییر را نیز افشا می‌کند. PUT /v1/admin/hubs/{hubEnvironmentId}/zones/{zoneId}/placements/{slotId} برای set یا replace بدنهٔ { destinationEnvironmentId, reason } و DELETE روی همان مسیر برای clear بدنهٔ { reason } می‌گیرد. توکن Project رد می‌شود. دلیل باید ۳ تا ۱۰۰۰ نویسه باشد، اسلات fixed تغییرناپذیر است، Hub نمی‌تواند مقصد خودش باشد و مقصد باید در Hub یکتا و یک Environment فعال، منتشرشده و عمومی کاتالوگ باشد. mutation و audit به‌صورت اتمیک commit می‌شوند.

مسیر /placements در Admin Panel و alamr admin hubs از همین API مدیریتی استفاده می‌کنند. برای جریان اپراتور و رفتار fail-closed، راهنمای Hub را ببینید.

lifecycle انتشار هدایت‌شده با سرور#

هر PublicationSubmission فیلد allowedActions مخصوص درخواست‌کننده دارد. هر ورودی action ID، status مقصد و لازم‌بودن reason را اعلام می‌کند. Manage و CLI فقط همین actionها را نمایش یا اجرا می‌کنند و transition را از status یا flag admin سمت client بازسازی نمی‌کنند. endpointهای checks، submit و review از مسیر transition عمومی جدا هستند و target frozen و state مورد انتظار را حفظ می‌کنند.

گردش‌کار بازبینی نسخهٔ ۲، مالکیت انسانی و مرز نهایی انتشار را پایدار می‌کند. GET /v1/admin/review-work-items/{submissionId}/claim مالک فعلی و نسخهٔ optimistic را برمی‌گرداند. بازبین مجاز با POST روی همین مسیر و expectedVersion تخصیص را claim می‌کند و با DELETE آن را آزاد می‌کند. برای پروندهٔ زنده، همین تراکنش تخصیص میزبان نسخهٔ جاری را با مالک و شمارهٔ یکسان ثبت می‌کند. claim تازه مهلت ۳۰ دقیقه‌ای دارد؛ خواندن یا تکرار توسط همان مالک مهلت را تمدید نمی‌کند و تکرار هم شمارهٔ جاری را می‌خواهد. تمدید با آزادسازی و claim تازه انجام می‌شود. آزادسازی claim قدیمی فاقد تخصیص میزبان فقط رکورد آزادشده ایجاد می‌کند و مجوز فعال گذشته بازسازی نمی‌شود. فقط بازبین تخصیص‌یافته می‌تواند POST /v1/admin/review-work-items/{submissionId}/decision را فراخوانی کند. این درخواست به نسخهٔ تخصیص و هر دو چکیدهٔ ثابت متصل است. رد کردن نیازمند دسته، خلاصه و دست‌کم یک اقدام اصلاحی قابل اجراست.

تاریخچهٔ تصمیم‌های بسته در GET /v1/admin/review-cases/{submissionId}/history خواندنی می‌ماند. شواهد ساختاریافتهٔ خودکارسازی از GET /v1/admin/review-work-items/{submissionId}/evidence در دسترس است. نبود baseline برای diff، پیکربندی‌نشدن تحلیل AI و نبود شواهد رفتاری وضعیت‌های صریح هستند؛ رجیستری یافتهٔ ساختگی تولید نمی‌کند و نبود خودکارسازی را قبولی تلقی نمی‌کند.

runner آزمایشگاه Plugin در زمان بازبینی، شواهد رفتاری را با POST /v1/admin/review-work-items/{submissionId}/evidence/behavioral (ReviewBehavioralEvidenceSubmissionRequest) و با اختیار بازبین ثبت می‌کند. درخواست به هر دو چکیدهٔ مورد انتظار و یک ReviewBehavioralReceipt متصل است که چکیده‌ها و شناسهٔ ارسال خودِ رسید باید با ارسال ذخیره‌شده مطابقت داشته باشند؛ عدم تطابق رد می‌شود و هرگز ذخیره نمی‌شود. ذخیره‌سازی فقط-درج است و با sha256 از JSON متعارف رسید نشانی‌دهی می‌شود، بنابراین ارسال دوبارهٔ همان اجرا به‌جای ساخت ردیف دوم (201) همان ردیف موجود (200) را برمی‌گرداند. تازه‌ترین رسید از طریق عضو الزامی behavioral در پاسخ شواهد خوانده می‌شود و حضور آن روی آیتم‌های کاری انتشارِ Plugin به‌صورت behavioralEvidence نمایان است. شواهد رفتاری تصمیم‌ساز نیست: یافته‌ها همان قالب مشورتی موجود را دارند و هرگز تأیید ثبت نمی‌کنند (ADR-0081).

ReviewBehavioralReceipt یک union سازگار با گذشته است. نسخهٔ schema ۱ رسید تاریخی و تغییرناپذیرِ browser/Playwright است. اجراهای تازه نسخهٔ schema ۲ را می‌سازند: Plugin ثابت داخل باندل تحویل‌شدهٔ Plugin Lab در کلاینت Desktop mount می‌شود و رسید، میزبان Desktop، lane رندر، نسخهٔ Electron و نسخهٔ کلاینت را ثبت می‌کند. هر دو نسخه خواندنی می‌مانند و producer حق ندارد شاهد قدیمی مرورگر را به‌عنوان شاهد Desktop بازنام‌گذاری کند (ADR-0099).

تأیید، به‌طور اتمیک یک PublicationHandoff متصل به رکورد تأیید و چکیده‌های دقیق می‌سازد. مدیران پلتفرم handoffها را از GET /v1/admin/publication-handoffs و GET /v1/admin/publication-handoffs/{handoffId} می‌خوانند. یک مدیر دوم ــ هرگز خود تأییدکننده ــ آن را با نسخهٔ optimistic claim می‌کند و سپس POST /v1/admin/publication-handoffs/{handoffId}/publish را همراه نسخهٔ claim‌شده و هر دو چکیدهٔ دقیق فراخوانی می‌کند. انتشار و تکمیل handoff در یک تراکنش commit می‌شوند؛ تکرار درخواست handoff تکمیل‌شده را برمی‌گرداند و دوباره منتشر نمی‌کند.

فقط در توسعهٔ non-production، کلاینت دسکتاپ می‌تواند جدیدترین loopback draft ساخته‌شده را پیش از publication باز کند. این کپی در Catalog دیده نمی‌شود و هویت دقیق باندل، origin اختصاص‌یافتهٔ پلتفرم، پذیرش bridge و Runtime fence همچنان اجباری‌اند. draft یک OAuth client میزبانی‌شده نزد ناشر نیست و handshake محصول منتشر نمی‌کند.

اختیار Runtime و Host اپ#

GET /v1/runtime/apps نسخه‌های در دسترس Runtime را فهرست می‌کند ولی مجوز اجرا نمی‌سازد. ویجت پیش از اجرا POST /v1/runtime/apps/capabilities/assess را صدا می‌زند. پاسخ consent_required شامل challenge کوتاه‌عمر، digest تغییرناپذیر ارزیابی، disclosureهای required/optional و یک ceremonyUrl است. تصمیم روی همان نشانی و روی مبدأ خودِ Registry گرفته می‌شود و راه دیگری برای ثبتش نیست: تصمیمی که از درون سند محیط فرستاده شود، تصمیمی است که خودِ محیط هم می‌توانست بفرستد.

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

اپ متصل‌شده می‌تواند با bearer نشست App خودش و POST /v1/apps/capabilities/request دقیقاً یک قابلیت اختیاریِ اعلام‌شده در manifest تغییرناپذیرش را دوباره مطرح کند. پاسخ موفق یا already_granted است یا یک ceremonyUrl کوتاه‌عمر روی مبدأ رجیستری می‌دهد؛ این route تصمیمی ثبت نمی‌کند و قابلیت required یا اعلام‌نشده را رد می‌کند.

POST /v1/runtime/apps/launch نسخهٔ دقیق و نگه‌داشته‌شدهٔ App، PKCE challenge تولیدشده در میزبان، nonce، display mode، نسل میزبان و digest مجموعهٔ قابلیت را می‌گیرد. رجیستری lease فعال، انتشار، origin باندل اختصاص‌یافتهٔ پلتفرم، سازگاری، Host Service محیط و رضایت را دوباره بررسی و کد یک‌بارمصرف می‌دهد.

SDK در origin اختصاص‌یافتهٔ پلتفرم کد را در POST /v1/apps/session/exchange مبادله می‌کند: برای App فقط-frontend مستقیم و برای App دارای اعلان backend از راه بک‌اند خود App. پاسخ کوتاه‌عمر دارای هویت جفتی اپ، نسخهٔ منبع و والد، display mode، نسل و فقط قابلیت و دادهٔ grant‌شده است. POST /v1/apps/session/introspect برای بک‌اند همان اپ است و با ازبین‌رفتن اختیار Runtime والد fail closed می‌شود.

Host Services endpoint HTTP ندارد. AppHost دسکتاپ کانال متصل به نسل را از راه bridge محدود preload منتقل می‌کند و Main مالک نشست پروتکل است؛ adapter فقط وب ممکن است همین کانال را با MessagePort نمایش دهد. رجیستری اختیار capability و محیط واقعیت context و navigation را نگه می‌دارند.

launch میان محیط‌ها یک پیوند عمیق است: alamr://environments/{environmentId}، که سیستم عامل به کلاینت دسکتاپ می‌دهد. یک جهان را نام می‌برد و چیز دیگری با خود نمی‌برد.

اینجا یک مسیر رجیستری بود، GET /v1/runtime/launch/{environmentId}. entry.url مقصد را می‌خواند، al_amr_return_target، al_amr_focus_location و یک al_amr_try_plugin اعتبارسنجی‌شده را به آن می‌افزود و مرورگر را با ۳۰۲ به آنجا می‌فرستاد. ADR-0094 چیزی برایش باقی نمی‌گذارد: جهان بایت است، نشانی‌اش alamr-env://{environmentId} است و هیچ مرورگری این scheme را نمی‌شناسد. focus_location و مشخصات try-it روی همان ریدایرکت سفر می‌کردند و روی پیوند عمیق سفر نمی‌کنند؛ «امتحان کنید» حالا محیط آزمایشگاه را باز می‌کند نه محیط آزمایشگاه را با یک نسخهٔ سوارشده، و تصمیم واجدشرایطیِ ADR-0082 دست‌نخورده است.

API اعلان Runtime و workload#

نوشتن V1 اپ یک Occurrence تغییرناپذیر است که روی Subject جاری reduce می‌شود. POST /v1/apps/notifications/signal از bearer نشست اپ استفاده و NotificationOccurrenceSignalRequest سخت‌گیرانه را می‌پذیرد:

{
  "eventId": "edit_01J9Y7K6",
  "eventType": "document.changed",
  "occurredAt": "2026-09-04T10:42:17.000Z",
  "effect": "raise",
  "subjectKey": "document:doc_42",
  "revision": 81,
  "content": {
    "kind": "source",
    "title": "Document updated",
    "action": { "kind": "open_app" }
  }
}

revision دقیق و تغییرناپذیر منبع باید برای event یک policy بازبینی‌شده اعلام کند: family بسته، policy Subject از نوع keyed/source_monotonic یا per_occurrence/event_identity، effectهای مجاز، preferenceKey پایدار، policy Pulse از نوع new_attention | none و presentation از نوع source یا platform_template نام‌دار. event کلیددار subjectKey و revision monotonic دامنهٔ منبع را با هم می‌دهد؛ event per-occurrence هیچ‌کدام را ندارد. منبع در retry باید همان eventId را تکرار کند. duplicate یا revision قدیمی no-op پذیرفته‌شده است. occurredAt اجباری است و در retry تغییر نمی‌کند؛ رجیستری زمان دریافت را جدا ثبت می‌کند. route هم‌حساب رخداد قدیمی‌تر از ۳۰ روز یا بیش از پنج دقیقه در آینده را رد می‌کند؛ route تفویض‌شده envelope accepted-only را نگه می‌دارد و همان مورد را no-op بدون محتوا می‌گیرد. منبع چندگیرنده‌ای به‌جای افشای شناسهٔ خام مشترک، subjectKey پایدار و recipient-scoped می‌سازد.

محتوای source-authored حتی برای App first-party سقف پیش‌فرض file دارد. template بازبینی‌شدهٔ first-party به‌نام message_waiting فقط actorSub جفتی اپِ اختیاری می‌گیرد؛ رجیستری title را از roster خود گیرنده می‌سازد و open_app را اضافه می‌کند. منبع متن template، preview، category، صدا یا treatment را انتخاب نمی‌کند. پاسخ signal باز 202 { "accepted": true } است و treatment یا وضعیت recipient را نمی‌دهد. client عمومی App همان call را با appClient.signalNotificationOccurrence(occurrence) ارائه می‌کند.

App فقط محتوای منبع را که واقعاً تا revision شناخته‌شده نمایش داده acknowledge می‌کند:

POST /v1/apps/notifications/acknowledge
{
  "acknowledgements": [
    {
      "eventType": "document.changed",
      "subjectKey": "document:doc_42",
      "throughRevision": 81
    }
  ]
}

request بین ۱ تا ۱۰۰ entry یکتا دارد و فقط 202 { "accepted": true } برمی‌گرداند. رجیستری بیشینهٔ watermark را حتی پیش از وجود Subject ثبت می‌کند. بازکردن App یا کلیک روی اعلان acknowledgement نیست.

App حساب‌دار با background delegated credential opaque کاربر جاری را از این endpointها می‌سازد و revoke می‌کند:

  • POST /v1/apps/notifications/delegation؛
  • DELETE /v1/apps/notifications/delegation.

delegation plaintext فقط هنگام ساخت برمی‌گردد. بک‌اند App آن را با Project token تاریخ‌دار دارای scope notifications:publish ترکیب می‌کند و POST /v1/workloads/apps/notifications/signal را صدا می‌زند. credential نامعتبر workload، scope کم و request بدشکل ممکن است رد شود. پس از پذیرش این واقعیت‌های خارجی، همهٔ outcomeهای وابسته به recipient همان 202 { "accepted": true } هستند و count، وجود recipient، consent، treatment یا read state را افشا نمی‌کنند. @al-amr/backend این مسیر را با createAppNotificationWorkloadClient(...).signal(delegation, occurrence) ارائه می‌دهد.

Desktop Shell و Environment Widget دو خواندن متفاوت دارند:

  • device plane: GET /v1/account/notifications و POST به /v1/account/notifications/read و /dismiss برای Center کامل حساب؛
  • Runtime فعال: GET /v1/runtime/notifications و POST به /v1/runtime/notifications/read و /dismiss که فقط به Environment جاری محدود است.

هر دو list از pagination نوع keyset با cursor opaque و limit محدود استفاده می‌کنند. فقط اگر page بعدی ممکن باشد nextCursor می‌آید. ردیف dismissed در feed عادی نیست؛ dismiss episode جاری Attention را مصرف می‌کند و raise جدیدتر می‌تواند Subject را باز کند. count مستقل از page فعلی است.

پاسخ unseenCount را برای همهٔ Subjectهای فعال دیده‌نشده، شامل mute، از attentionCount جدا می‌کند؛ دومی mute را حذف می‌کند و badge/attention را می‌سازد. unreadCount اجباری قدیمی alias attentionCount است. ردیف V1 ممکن است projectionVersion و subjectRevision اضافه کند؛ schema Inbox tolerant است تا Widgetهای immutable آن را نادیده بگیرند. request جدید read و dismiss یک map projectionVersions با کلید notificationId می‌فرستد تا action قدیمی projection تازه‌تر را مصرف نکند. map اختیاری raisedAt fallback timestamp برای readerهای immutable است.

eventهای Presence و account-control فقط hint بدون محتوا هستند و جای list مجاز را نمی‌گیرند. reconciliation حاصل از list یا reconnect هیچ Pulse گذرایی بازسازی نمی‌کند. Shell News کامل حساب را می‌گیرد؛ Environment فقط ردیف‌های محدود خودش و attention سه‌کلمه‌ای none | waiting | urgent را می‌بیند، نه محتوای حساب یا count.

مسیرهای publish اولیه به‌شکل API legacy و additive باقی می‌مانند:

  • POST /v1/runtime/notifications/publish برای Environment فعال؛
  • POST /v1/apps/notifications/publish برای App باز و environment-hosted؛
  • POST /v1/workloads/apps/notifications/publish برای backend تفویض‌شدهٔ App؛
  • POST /v1/workloads/environments/notifications/publish با Environment Backend Grant کوتاه‌عمر exact-endpoint و scope notifications.publish@1.0.0.

payload قدیمی plain text محدود، action معنایی بسته، category، TTL و dedupeKey را نگه می‌دارد؛ category هیچ اختیار family، صدا یا Pulse نمی‌دهد. write قدیمی semantics مربوط به Subject revision/watermark V1 را نمی‌گیرد. POST /v1/apps/notifications/read acknowledgement source-wide قدیمی برای caller immutable باقی می‌ماند.

این endpointها فقط News ماندگار می‌سازند. CallAlert زنده و deadline-bound یک projection مستقل device-plane است و push خارجی، ایمیل و پیامک در API اعلان V1 پیاده نشده‌اند.

Grant بک‌اند Environment#

POST /v1/runtime/environment-backend/grants یک Runtime فعال را با اختیار endpoint دقیق اعلام‌شده در revision تغییرناپذیر Environment مبادله می‌کند. درخواست فقط scope و context اعلام‌شده را می‌فرستد و Registry actor، origin، نقش، endpoint و Runtime fence را استخراج می‌کند. backend با POST /v1/runtime/environment-backend/grants/introspect پس از logout، expiry، revocation یا supersede شدن fail closed می‌شود.

این endpointها event همایش، assignment برگزارکننده یا هیچ رکورد دامنه‌ای Environment را در Registry ذخیره نمی‌کنند؛ آن state متعلق به backend ناشر است.

تنظیمات Runtime#

GET /v1/runtime/plugins/{pluginId}/settings objectها را به‌صورت بازگشتی و با ترتیب اولویت مستند resolve می‌کند. layers ورودی دقیق plugin_default، environment، user و user_environment را برمی‌گرداند؛ مقدار ارث‌رسیده در لایهٔ ماندگار کاربر کپی نمی‌شود. درخت provenance نیز scope برندهٔ هر مقدار نهایی را نشان می‌دهد. leaf ماندگاری که دیگر با schema release نصب‌شده سازگار نیست، به‌شکل fail-safe حذف می‌شود و به کد Plugin نمی‌رسد.

مقدار etag بدنه با header استاندارد ETag یکسان است. client باید این tag تجمیعی را به‌صورت If-Match همراه PUT و DELETE بفرستد تا تغییر هم‌زمان هر لایه با 412 رد شود. tag برای هر write نسل غیرتکراری دارد، پس reset و ساخت دوباره precondition قدیمی را معتبر نمی‌کند. reset idempotent است و حذف دوبارهٔ لایهٔ خالی با ETag جاری 204 می‌دهد. guest می‌تواند settings نهایی را بخواند، اما write ماندگار user و user_environment به حساب نیاز دارد.

OpenAPI تولیدشده در Registry و در https://registry.al-amr.com/v1/openapi.json منتشر می‌شود. خطاها code ماشینی پایدار و request ID دارند. اتوماسیون CLI باید --json را با process exit code مستند ترکیب کند.

آرتیفکت‌های پکیج#

ثبت npm خارجی فقط نام package را می‌پذیرد. Registry نسخه را از Plugin release تغییرناپذیر می‌گیرد و registry URL، tarball URL و SHA-512 را خودش resolve و اعتبارسنجی می‌کند:

{ "provider": "npm", "packageName": "@publisher/plugin" }

پس از validation، Registry دقیقاً همان bytes را در store content-addressed الامر mirror می‌کند. URL نصب منتشرشده حتی پس از تغییر metadata بالادستی به همان SHA-512 بازبینی‌شده متصل می‌ماند.

artifact مدیریت‌شده از مسیر binary /artifacts/managed بارگذاری می‌شود. مالک پیش از publication از POST /v1/projects/{projectId}/artifacts/{artifactId}/draft-download-url یک URL خصوصی ۶۰ تا ۹۰۰ ثانیه‌ای می‌گیرد. نشست cookie به CSRF و Project token به project:read نیاز دارد. artifact منتشرشده ناشناس، immutable و publication-gated می‌ماند.

CLI بارگذاری managed را پیش‌فرض نگه می‌دارد. برای package موجود در npm عمومی پیکربندی‌شده، alamr test --provider npm و سپس alamr publish --provider npm اجرا می‌شود.

تصویر معرفی خصوصی ناشر#

Main کلاینت تصویر مدیریت‌شدهٔ همان submission را از مسیر GET /v1/projects/{projectId}/publication-submissions/{submissionId}/presentation-media و endpoint متناظر /bytes می‌خواند. این مسیرها آیکون App، کاور Environment و آیکون Plugin را پشتیبانی می‌کنند. هر دو به نشست مدیریت دسکتاپ ثبت‌شده با scope publication:read-own و عضویت پایدار در پروژهٔ همان submission نیاز دارند؛ ادمین پلتفرم بودن به‌تنهایی مجوز نمی‌دهد. بایت‌های منتشرنشده خصوصی می‌مانند و از مسیر عمومی تصاویر ارائه نمی‌شوند.

اعتبارنامه‌های CI#

هر Project token باید expiresAt صریح و آینده داشته باشد و بیش از ۳۶۶ روز عمر نکند. Console برای token جدید مقدار پیش‌فرض ۳۰ روزه می‌گذارد.