{
  "title": "مدل مجوزها — منبع حقیقت و مالکیت توزیع‌شده",
  "slug": "team/platform/permission-model",
  "url": "/docs/team/platform/permission-model",
  "frontmatter": {
    "layout": "doc",
    "title": "مدل مجوزها — منبع حقیقت و مالکیت توزیع‌شده",
    "description": "معماری نهایی مدل مجوزها — از تعریف در Proto تا اعمال در CI، با مالکیت توزیع‌شده متادیتا",
    "version": "1.0.0",
    "status": "ACTIVE",
    "author": "Platform Team",
    "owner": "Platform Team",
    "created_at": "2026-07-24",
    "updated_at": "2026-07-24",
    "tags": ""
  },
  "sections": [
    {
      "level": 1,
      "heading": "مدل مجوزها — منبع حقیقت و مالکیت توزیع‌شده",
      "content": "**Permission Model — Source of Truth & Distributed Ownership**\n\n> این سند معماری نهایی مدل مجوزهای پلتفرم NONS را شرح می‌دهد: یک منبع حقیقت برای کلیدها (Proto)، مالکیت توزیع‌شده متادیتا در هر سرویس، و زنجیره خودکار از Proto تا CI.\n\n---"
    },
    {
      "level": 2,
      "heading": "۱. اصول معماری",
      "content": ""
    },
    {
      "level": 3,
      "heading": "اصل اول — Proto منبع حقیقت تعریف کلیدهاست",
      "content": "فایل `contracts/permissions.proto` (در `nons-api/`) تنها جایی است که کلیدهای مجوز تعریف می‌شوند. هیچ سرویس یا فرانت‌اندی حق تعریف کلید جدید خارج از Proto را ندارد."
    },
    {
      "level": 3,
      "heading": "اصل دوم — متادیتا نزد تیم صاحب هر سرویس زندگی می‌کند",
      "content": "هر سرویس فایل `permissions.meta.yaml` خود را دارد. IAM فقط کلیدهای مربوط به خود را نگهداری می‌کند. این یعنی:\n\n- **تیم marketplace** مالک `products.*` است\n- **تیم order** مالک `orders.*` است\n- **تیم wallet** مالک `wallets.*` است\n- **تیم IAM** مالک `admin.*`، `*.read` و `tickets.*` است (موقت)"
    },
    {
      "level": 3,
      "heading": "اصل سوم — یک کلید = یک مالک",
      "content": "هر کلید مجوز در Proto باید دقیقاً یک فایل YAML مالک داشته باشد. اگر صفر باشد (orphan) CI رد می‌کند. اگر دو تا باشد (collision) CI رد می‌کند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۲. Pipeline کامل",
      "content": "```\ncontracts/permissions.proto   (SSOT — Source of Truth)\n    │\n    ├── buf generate ──→ core/platform/*.pb.go          (FINAL, committed)\n    ├── buf generate ──→ packages/contracts/src/*.ts     (FINAL, committed)\n    ├── buf generate ──→ packages/types/src/*.ts         (FINAL, committed)\n    ├── buf generate ──→ packages/events/src/*.ts        (FINAL, committed)\n    ├── buf generate ──→ gen/openapi/*.swagger.json      (INTERMEDIATE, gitignored)\n    │       │\n    │       └── openapi-typescript ──→ packages/contracts/src/iam-sdk.ts (FINAL, committed)\n    │\n    ├── generate-iam-permission-keys.mjs ──→ permission_keys_gen.go\n    │\n    └── services/*/permissions.meta.yaml\n            │\n            └── aggregate-permission-meta.mjs ──→ permission_meta_aggregated.go (FINAL)\n```"
    },
    {
      "level": 3,
      "heading": "تفکیک FINAL و INTERMEDIATE",
      "content": "| Artifact | Type | در git؟ | CI check؟ |\n|----------|------|---------|-----------|\n| `.pb.go` (Go protobuf) | FINAL | ✅ committed | `git diff --exit-code` |\n| `.ts` (TypeScript) | FINAL | ✅ committed | `git diff --exit-code` |\n| `iam-sdk.ts` | FINAL | ✅ committed | `git diff --exit-code` |\n| `permission_keys_gen.go` | FINAL | ✅ committed | `git diff --exit-code` |\n| `permission_meta_aggregated.go` | FINAL | ✅ committed | `git diff --exit-code` |\n| `gen/openapi/*.swagger.json` | INTERMEDIATE | ❌ gitignored | مصرف مستقیم در pipeline |\n\n---"
    },
    {
      "level": 2,
      "heading": "۳. CI Validation Chain",
      "content": "هر commit توسط ۳ لایه CI محافظت می‌شود:"
    },
    {
      "level": 3,
      "heading": "لایه ۱ — Proto lint & breaking",
      "content": "```\nnpx buf lint contracts          — قالب‌بندی و قراردادهای Proto\nnpx buf breaking contracts      — عدم تغییرات breaking (حذف فیلد، شماره‌گذاری مجدد)\n```"
    },
    {
      "level": 3,
      "heading": "لایه ۲ — Codegen & ownership validation",
      "content": "```\npnpm codegen                    — بازتولید همه artifactها\n  ├── aggregate-permission-meta.mjs\n  │   ├── هر Proto key = دقیقاً ۱ YAML owner  (رد orphan + collision)\n  │   ├── هر YAML key = معتبر در Proto          (رد کلید جعلی)\n  │   └── متادیتا کامل: name, description, group\n  └── prettier                   — فرمت کدهای تولیدشده\n```"
    },
    {
      "level": 3,
      "heading": "لایه ۳ — Diff sync",
      "content": "```\ngit diff --exit-code            — تضمین هماهنگی artifactهای committed با Proto\ngo test ./core/platform/...     — تست تطابق Proto key ↔ ۲۱ کلید\ngo test ./services/iam/...      — تست تطابق aggregated metadata ↔ Proto keys\n```\n\n---"
    },
    {
      "level": 2,
      "heading": "۴. مالکیت فعلی",
      "content": "| سرویس | کلیدها | مالک |\n|-------|--------|------|\n| `iam-service` | `admin.access`, `roles.read`, `permissions.read`, `capabilities.read`, `restrictions.read`, `policies.read`, `workspaces.read`, `audit.read`, `events.read`, `settings.read` | IAM Team |\n| `iam-service` (temporary) | `tickets.read`, `tickets.resolve` | IAM Team (تا ایجاد tickets-service) |\n| `marketplace-service` | `products.create`, `products.update`, `products.delete`, `products.publish` | Marketplace Team |\n| `order-service` | `orders.create`, `orders.cancel` | Order Team |\n| `wallet-service` | `wallets.read`, `wallets.withdraw` | Wallet Team |\n| `user-service` | `users.read` | User Team |"
    },
    {
      "level": 3,
      "heading": "نکته: نام سرویس ≠ namespace کلید",
      "content": "نام پوشه سرویس (مثلاً `order-service`) لزوماً با namespace کلید (مثلاً `orders.*`) یکی نیست. این عمدی است — نام پوشه منطبق بر نام مخزن سرویس است، namespace کلید از قرارداد IAM پیروی می‌کند.\n\n---"
    },
    {
      "level": 2,
      "heading": "۵. افزودن مجوز جدید",
      "content": ""
    },
    {
      "level": 3,
      "heading": "مراحل",
      "content": "1. **افزودن enum value به `contracts/permissions.proto`** (`PermissionKey`)\n   - نیاز به بررسی تیم پلتفرم\n   - مقدار عددی جدید (از آخرین مقدار + ۱)\n\n2. **اجرای `pnpm codegen`** — بازتولید خودکار Go/TS/SDK/OpenAPI\n\n3. **افزودن metadata به `services/<your-service>/permissions.meta.yaml`**\n   ```yaml\n   # Owner: <your-service>\n   - key: yourresource.newaction\n     name: Human Readable Name\n     description: One-sentence explanation\n     group: yourresource\n   ```\n   - فقط نیاز به بررسی تیم خودتان\n   - CI به‌صورت خودکار orphan/collision را بررسی می‌کند\n\n4. **ثبت در IAM DB seed** (`002_seed_defaults.sql`)\n\n5. **افزودن برچسب فارسی/انگلیسی** در `admin-panel/locales/`\n\n6. **همه تست‌ها باید پاس شوند**"
    },
    {
      "level": 3,
      "heading": "حذف مجوز",
      "content": "- کلید را از Proto حذف کنید (breaking change — نیاز به major version)\n- از YAML سرویس حذف کنید\n- از IAM DB seed حذف کنید\n- از localeها حذف کنید\n\n---"
    },
    {
      "level": 2,
      "heading": "۶. مستندات مرتبط",
      "content": "- [شروع سریع بک‌اند](/docs/team/backend/get-started)\n- [شروع سریع فرانت‌اند](/docs/team/frontend/get-started)\n- [استاندارد قرارداد مجوز](/docs/team/platform/standards/permission-contract-standard)\n- [استاندارد همگام‌سازی SDK](/docs/team/platform/standards/permission-and-sdk-sync-standard)\n- [Blueprint سرویس IAM](/docs/team/backend/services/iam-service)\n- [تغییرات معماری (Phase 1→5)](/docs/team/platform/ADR/ADR-Platform-004)\n\n---"
    },
    {
      "level": 2,
      "heading": "A. پیوست — تاریخچه معماری",
      "content": "| فاز | منبع کلیدها | مالکیت متادیتا | مکانیزم |\n|-----|-------------|----------------|---------|\n| ۱ | IAM DB seed | تک فایل در IAM | دستی |\n| ۲ | `permissions_meta.yaml` (دستی) | تک فایل در IAM cmd/ | دستی |\n| ۳ | Proto (enum فقط) | تک فایل در IAM cmd/ | semi-auto |\n| ۴ | Proto + OpenAPI + SDK | تک فایل در IAM cmd/ | codegen |\n| **۵ (فعلی)** | **Proto** | **توزیع‌شده per-service YAML** | **کامل خودکار + CI** |"
    }
  ]
}