# Manifest Reference

## Schema
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | ✓ | Reverse-domain unique ID |
| `name` | string or map | ✓ | Display name, supports locales |
| `description` | string |  | Short summary |
| `version` | string | ✓ | SemVer |
| `sdkapi` | number | **Recommended** | SDK API version, format YYMMDD (e.g., 260626) |
| `category` | string | Conditional | Category id synced with Nexus (e.g., `utilities`, `productivity`) (required when `sdkapi >= 260114`) |
| `main` | string | ✓ | Prelude script entry, usually `index.js` |
| `entry` | string | Legacy | Legacy init entry; do not use for new plugins |
| `preload` | string |  | Renderer preload file |
| `dev.enable` | boolean |  | Enable hot reload |
| `permissions` | object |  | Permission declarations, see below |
| `permissionReasons` | object |  | Reasons for permissions |
| `features[].acceptedInputTypes` | string[] |  | `text`, `image`, `files`, `html` |
| `features` | object[] |  | CoreBox commands, widgets, workflow nodes |
| `semanticAliases` | object[] |  | Semantic alias declarations with marker `App Semantic Alias Catalog / 应用语义别名目录` (recommended with `sdkapi >= 260626`) |

## SDK API Version (sdkapi)

The `sdkapi` field declares the SDK API version the plugin is compatible with. Format is `YYMMDD` (year-month-day).

- **Current version**: `260626` (2026-06-26)
- **Supported list**: `251212`, `260114`, `260121`, `260215`, `260225`, `260228`, `260428`, `260615`, `260626`
- **Not declared, invalid, unsupported, or below 251212**: Blocked by runtime with `SDKAPI_BLOCKED`
- **251212 ~ 260113**: Full permission enforcement enabled
- **Equal to or above 260114**: Requires `category` for UI grouping (in addition to 251212 baseline)
- **Equal to or above 260215**: Can use plugin SQLite SDK (`usePluginSqlite`)
- **Equal to or above 260228**: Plugin capability auth baseline is enabled
- **260428**: Supported marker; no additional runtime gate is introduced
- **260615**: Supported marker; no additional runtime gate is introduced
- **260626**: Current recommended marker; exposes `SemanticAliasSDK` and `semanticAliases` declarations without adding a SQLite schema change or extra runtime gate

New plugins should always declare the latest `sdkapi` version for complete permission protection.

## Semantic Aliases (semanticAliases)

Recognition marker: **App Semantic Alias Catalog / 应用语义别名目录**.

`semanticAliases` declares discoverability terms for a plugin, feature, or provider: short aliases, category terms, Chinese/English synonyms, and common abbreviations. It is manifest metadata in V1 so developers can keep aliases in one structure; runtime integration should still write through existing `features[].keywords`, `searchTokens`, or Search Provider paths. It does not automatically add uninstalled apps or external website results.

:::TuffCodeBlock{lang="json"}
---
code: |
  {
    "sdkapi": 260626,
    "semanticAliases": [
      {
        "id": "todo.create",
        "target": "todo.new",
        "label": "Create Todo",
        "aliases": ["todo", "task"],
        "categories": ["office", "办公"],
        "synonyms": ["待办", "任务"]
      }
    ]
  }
---
:::

## Permissions

The permission system controls plugin access to sensitive APIs. See [Permission API docs](/docs/dev/api/permission) for details.

**Declaration Format**

:::TuffCodeBlock{lang="json"}
---
code: |
  "permissions": {
    "required": ["clipboard.read", "network.internet"],
    "optional": ["storage.shared"]
  },
  "permissionReasons": {
    "clipboard.read": "Read text from clipboard for translation",
    "network.internet": "Connect to translation API"
  }
---
:::

**Available Permissions**

| Permission ID | Risk | Description |
|--------------|------|-------------|
| `fs.read` | Medium | Read files |
| `fs.write` | High | Write files |
| `fs.execute` | High | Execute files |
| `clipboard.read` | Medium | Read clipboard |
| `clipboard.write` | Low | Write clipboard (auto-granted) |
| `network.local` | Low | Local network |
| `network.internet` | Medium | Internet access |
| `network.download` | Medium | Download files |
| `system.shell` | High | Execute commands |
| `system.notification` | Low | System notifications |
| `system.tray` | Medium | System tray |
| `intelligence.basic` | Low | Basic intelligence |
| `intelligence.admin` | High | Admin intelligence |
| `intelligence.agents` | High | Intelligence agents |
| `storage.plugin` | Low | Plugin storage (auto-granted) |
| `storage.shared` | Medium | Shared storage |
| `storage.sqlite` | Medium | Plugin SQLite database access |
| `window.create` | Low | Create windows (auto-granted) |
| `window.capture` | High | Screen capture |

## Example
:::TuffCodeBlock{lang="json"}
---
code: |
  {
    "id": "com.tuff.todo",
    "name": {
      "default": "Todo",
      "zh-CN": "Text"
    },
    "description": "Capture and sync todos",
    "version": "1.3.0",
    "sdkapi": 260626,
    "category": "utilities",
    "main": "index.js",
    "features": [
      {
        "id": "todo.new",
        "name": "Create Todo",
        "desc": "Create a local todo from input text",
        "keywords": ["todo", "task", "待办", "任务"],
        "push": true,
        "acceptedInputTypes": ["text", "files"],
        "commands": [
          { "type": "over", "value": ["todo", "task"] }
        ]
      }
    ],
    "semanticAliases": [
      {
        "id": "todo.create",
        "target": "todo.new",
        "aliases": ["todo", "task"],
        "categories": ["office", "办公"],
        "synonyms": ["待办", "任务"]
      }
    ],
    "permissions": {
      "required": ["clipboard.read", "storage.sqlite"],
      "optional": ["storage.shared"]
    },
    "permissionReasons": {
      "clipboard.read": "Read todo content from clipboard",
      "storage.sqlite": "Store todo data in local SQLite"
    }
  }
---
:::

## Validation Checklist
- `id` is required and uses a lowercase reverse-domain identifier with at least three safe segments.
- `name` is a lowercase slug and `version` is strict SemVer.
- `sdkapi` must be a supported marker; `category` is required at the documented SDK threshold.
- Prelude plugins use `main: "index.js"`; UI-only plugins may omit `main`.
- `permissions` uses `{ required: string[], optional: string[] }` and every id must exist in the permission registry.
- Packaged builds disable `dev.enable`/`dev.source`, clear the dev address, and bind every regular file through `_files`.
- Nexus rejects unsafe archive paths/types, duplicate or case-colliding entries, identity/version mismatches, and package limits before upload.

## Frequent Issues
| Issue | Fix |
| --- | --- |
| Missing main on a Prelude plugin | Set `main` to `index.js` and ensure the file exists; UI-only plugins do not need a Prelude entry. |
| Still using `entry` / `init(ctx)` | Migrate to the Prelude model through [Plugin Development Workflow](../getting-started/plugin-workflow.en.mdc). |
| Excessive permissions | Request only what you truly need, especially for v1. |
| Keyword collisions | Namespace features like `todo.*` to avoid conflicts. |
