{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.revenexx.com/manifest.schema.json",
  "title": "revenexx App Manifest",
  "description": "The contract between a revenexx App and the Platform. Declares the app's identity, dependencies, permissions, and events.",
  "type": "object",
  "required": ["name", "vendor", "version", "title"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "description": "Reference to this JSON Schema for editor validation and autocompletion."
    },
    "name": {
      "type": "string",
      "pattern": "^[a-z][a-z0-9-]*$",
      "description": "Unique app identifier. Lowercase, alphanumeric with hyphens. Used as the app's internal ID across Console, PostgREST, and Cockpit."
    },
    "vendor": {
      "type": "string",
      "description": "App publisher. Use 'revenexx' for core apps, partner or organization name for third-party apps."
    },
    "version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+",
      "description": "Semantic version (e.g. '1.0.0'). Used for dependency resolution and App Registry versioning."
    },
    "title": {
      "type": "string",
      "description": "Human-readable display name shown in Cockpit, Marketplace, and Console."
    },
    "description": {
      "type": "string",
      "description": "Short description of the app's purpose. Displayed in the Marketplace and Console."
    },
    "icon": {
      "type": "string",
      "description": "Path to the app icon relative to the app root. Used in Cockpit sidebar and Marketplace."
    },
    "dependencies": {
      "type": "object",
      "description": "Other apps this app depends on. Keys are app names, values are semver ranges. Auto-installed when the app is activated for a Tenant.",
      "additionalProperties": {
        "type": "string",
        "description": "Semver range (e.g. '^1.0', '>=2.0.0 <3.0.0')."
      }
    },
    "peerDependencies": {
      "type": "object",
      "description": "Apps required but NOT auto-installed. Console prompts the Customer to install them explicitly. Use for paid dependencies or optional features where the Customer should be aware of the cost/choice.",
      "additionalProperties": {
        "type": "string",
        "description": "Semver range (e.g. '^1.0')."
      }
    },
    "permissions": {
      "type": "array",
      "description": "Declares which entities (tables) the app can access and what operations are allowed.",
      "items": {
        "type": "object",
        "required": ["entity", "access"],
        "additionalProperties": false,
        "properties": {
          "entity": {
            "type": "string",
            "description": "The entity (table) name this permission applies to."
          },
          "access": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": ["read", "create", "update", "delete"]
            },
            "minItems": 1,
            "uniqueItems": true,
            "description": "Allowed operations on the entity."
          }
        }
      }
    },
    "events": {
      "type": "object",
      "description": "Event declarations for the platform event bus. Includes business events and platform lifecycle events.",
      "additionalProperties": false,
      "properties": {
        "emits": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$"
          },
          "uniqueItems": true,
          "description": "Events this app publishes (e.g. 'order.created', 'product.updated')."
        },
        "listens": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "uniqueItems": true,
          "description": "Events this app subscribes to. Includes business events (e.g. 'inventory.stock_changed') and platform lifecycle events ('app.installed', 'app.uninstalled', 'app.upgraded', 'tenant.provisioned')."
        }
      }
    },
    "policies": {
      "type": "array",
      "description": "Declarative allowlist of platform capabilities and external access this app requires. Apps get no access by default — every outbound call, event bus interaction, and storage operation must be declared here. Console shows these to the Customer at install time.",
      "items": {
        "$ref": "#/$defs/policy"
      }
    }
  },
  "$defs": {
    "policy": {
      "type": "object",
      "required": ["name"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "enum": [
            "outbound-access",
            "event-publish",
            "event-subscribe",
            "storage-read",
            "storage-write",
            "storage-read-write"
          ],
          "description": "Policy type. 'outbound-access' controls external HTTP calls. 'event-publish'/'event-subscribe' control event bus access. 'storage-*' control file storage bucket access."
        },
        "attrs": {
          "type": "object",
          "description": "Policy-specific attributes.",
          "properties": {
            "host": {
              "type": "string",
              "description": "For outbound-access: allowed hostname. Supports '{{tenant_domain}}' template variable which resolves to the Customer's configured domain."
            },
            "path": {
              "type": "string",
              "description": "For outbound-access: allowed URL path pattern. Supports wildcards (e.g. '/api/*')."
            },
            "events": {
              "type": "array",
              "items": { "type": "string" },
              "description": "For event-publish/event-subscribe: list of event names this policy covers."
            },
            "bucket": {
              "type": "string",
              "description": "For storage policies: bucket name or '*' for all buckets."
            }
          },
          "additionalProperties": false
        }
      }
    }
  }
}
