# AstrBot Plugin Market JSON Specification Specification version: 2026-06-27 Market JSON schema version: 1 ## 1. Scope This document defines the JSON format for AstrBot plugin market sources. ## 2. Terms - `PluginRecord`: One plugin entry in a market source. - `MarketMetadata`: Metadata for the market source itself. - `plugin_id`: The global plugin package identifier. - `metadata.yaml`: The plugin metadata file inside the installed plugin package. The terms `MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` are normative. ## 3. Document Format A plugin market source MUST be a UTF-8 JSON object. The root object MUST contain the reserved `$meta` key. All root keys other than `$meta` MUST be one of: - a `plugin_id` string - a `name` string under the compatibility exception in Section 5 Each root key other than `$meta` MUST map to a `PluginRecord` object: ```json { "$meta": { "schema_version": 1, "name": "AstrBot Official Plugin Market", "version": "2026-06-27", "homepage": "https://astrbot.app", "repository": "https://github.com/AstrBotDevs/AstrBot_Plugins_Collection" }, "owner/astrbot_plugin_foo": { "author": "owner", "name": "astrbot_plugin_foo", "version": "1.0.0", "repo": "https://github.com/owner/astrbot_plugin_foo", "desc": "Plugin description" }, "astrbot_plugin_bar": { "author": "owner", "version": "1.0.0", "repo": "https://github.com/owner/astrbot_plugin_bar", "desc": "Compatibility record using the root key as name" } } ``` The root object MUST NOT contain top-level metadata fields outside `$meta`. ## 4. Market Metadata `$meta` MUST NOT be interpreted as a `PluginRecord`. `$meta` MUST be a `MarketMetadata` object. `MarketMetadata` MUST contain: | Field | Type | Requirement | | ---------------- | ------- | --------------------------------------------------------------- | | `schema_version` | integer | Market JSON schema version. MUST be `1` for this specification. | `MarketMetadata` MAY contain: | Field | Type | Requirement | | ------------- | ------ | ------------------------------------------------------------------- | | `name` | string | Human-readable market source name. | | `version` | string | Market source data version. MUST NOT be used as the schema version. | | `homepage` | string | HTTPS homepage URL. | | `repository` | string | HTTPS repository URL for the market source data. | | `description` | string | Market source description. | | `updated_at` | string | ISO 8601 timestamp. | `$meta` MUST NOT be used for: - plugin identity - install source resolution - update source resolution - plugin security validation ## 5. Plugin Identity `plugin_id` is defined as: ```text metadata.author + "/" + metadata.name ``` For every `PluginRecord`: - The root key SHOULD equal `${author}/${name}`. - The root key MAY equal `name` under the compatibility exception below. - `author` MUST equal `metadata.yaml.author`. - `name` MUST equal `metadata.yaml.name`. - `plugin_id` MUST be globally unique across the AstrBot plugin ecosystem. - Two `plugin_id` values that are equal after lowercase normalization MUST NOT both exist. - `repo` MUST NOT be used as plugin identity. - `root_dir_name`, local directory names, display names, and registry names MUST NOT be used as plugin identity. `author` and `name`: - MUST be non-empty strings. - MUST be trimmed. - MUST NOT contain `/`. - MUST NOT contain ASCII control characters. - SHOULD be stable package identity values, not display names. ### 5.1. Name Key Compatibility Exception A root key MAY equal `metadata.yaml.name` instead of `${author}/${name}`. When this exception is used: - The root key MUST NOT contain `/`. - `author` MUST be present. - `name` MAY be omitted. - If `name` is present, it MUST equal the root key. - The root key MUST equal `metadata.yaml.name`. - Consumers MUST construct `plugin_id` as `${author}/${root_key}`. ## 6. Required Fields Each `PluginRecord` MUST contain: | Field | Type | Requirement | | --------- | ------ | ------------------------------------------------------------------------------------------------------------------------ | | `author` | string | Plugin author namespace. MUST match `metadata.yaml.author`. | | `name` | string | Plugin package name. MUST match `metadata.yaml.name`. MAY be omitted only when the root key equals `metadata.yaml.name`. | | `version` | string | Plugin version. MUST match `metadata.yaml.version`. | | `repo` | string | HTTPS GitHub repository URL used for source attribution and installation. | | `desc` | string | Default plugin description. | ## 7. Optional Fields | Field | Type | Requirement | | ------------------- | -------- | ---------------------------------------------------------------------------------- | | `display_name` | string | Human-readable plugin name. | | `short_desc` | string | Short description for compact UI. | | `download_url` | string | HTTPS ZIP download URL. Installed package identity MUST still match `author/name`. | | `logo` | string | HTTPS image URL or relative static asset path. | | `tags` | string[] | Tag list. | | `category` | string | Market category key. | | `support_platforms` | string[] | Supported platform keys. | | `astrbot_version` | string | AstrBot version requirement expression. | | `social_link` | string | Author or project HTTPS URL. | | `updated_at` | string | ISO 8601 timestamp. | | `i18n` | object | Localized display fields. | | `pinned` | boolean | Market recommendation flag. | | `stars` | integer | Non-negative star count or ranking signal. | | `download_count` | integer | Non-negative download count. | ### 7.1. Repository URL `repo` MUST be one of: ```text https://github.com/{owner}/{repository} https://github.com/{owner}/{repository}.git https://github.com/{owner}/{repository}/tree/{branch} ``` `owner`, `repository`, and `branch` MUST match: ```text [A-Za-z0-9_-]+ ``` `repo` MUST NOT be: - an HTTP URL - an SSH URL - a non-GitHub URL - a GitHub Enterprise URL - a GitHub file, release, pull request, issue, or subdirectory URL `download_url` MAY point to any HTTPS ZIP URL. ### 7.2. Localized Fields `i18n` keys SHOULD be BCP 47 locale tags. Each locale object MAY contain: ```json { "display_name": "Localized name", "desc": "Localized description", "short_desc": "Localized short description" } ``` ## 8. Reserved Fields Market sources MUST NOT use these fields inside `PluginRecord` objects: | Field | Reason | | -------------------------- | ---------------------------------------------------- | | `plugin_id` | Derived from `author/name`. | | `market_plugin_id` | Runtime/client compatibility field, not market data. | | `market_plugin_identifier` | Deprecated runtime/client compatibility field. | | `root_dir_name` | Local installation field. | | `local_plugin_name` | Local installation field. | | `install_method` | Local installation field. | | `registry_url` | Local installation field. | | `registry_name` | Local installation field. | | `installed_at` | Local installation field. | Consumers MUST NOT use reserved fields for plugin identity. The root key `$meta` is reserved for `MarketMetadata`. ## 9. Deprecated Fields These fields MAY be accepted by clients during migration, but new market sources MUST NOT emit them: | Deprecated Field | Replacement | | ------------------ | ------------------- | | `support_platform` | `support_platforms` | | `platform` | `support_platforms` | ## 10. Unknown Fields Consumers MAY preserve unknown fields. Consumers MUST NOT use unknown fields for: - plugin identity - install source resolution - update source resolution - security validation ## 11. Validation Rules A market source is valid only if all rules pass: 1. The root JSON value is an object. 2. `$meta` is present. 3. `$meta` is an object. 4. `$meta.schema_version` is the integer `1`. 5. Every root key other than `$meta` is either a `plugin_id` or a `name` key. 6. Every root value other than `$meta` is a `PluginRecord` object. 7. Every record contains all required fields except `name` MAY be omitted when the root key is a `name` key. 8. Every required string field is non-empty after trimming. 9. `author` and present `name` values do not contain `/`. 10. The root key equals `${author}/${name}` exactly, or the root key equals `name` exactly under the compatibility exception. 11. No two IDs are equal after lowercase normalization. 12. `repo` is an HTTPS GitHub repository URL. 13. If present, `download_url` is an HTTPS URL. 14. If present, `tags` is an array of strings. 15. If present, `support_platforms` is an array of strings. 16. If present, `stars` is a non-negative integer. 17. If present, `download_count` is a non-negative integer. 18. If present, `updated_at` is an ISO 8601 timestamp. 19. If present, `$meta.homepage` is an HTTPS URL. 20. If present, `$meta.repository` is an HTTPS URL. 21. If present, `$meta.updated_at` is an ISO 8601 timestamp. 22. The package resolved from `repo` or `download_url` contains `metadata.yaml`. 23. `metadata.yaml.author` equals `author`. 24. `metadata.yaml.name` equals `name`. 25. `metadata.yaml.version` equals `version`. ## 12. Installation Rules For market installation: 1. The client sends the selected `plugin_id` and registry source to the backend. 2. The backend resolves the `PluginRecord` from that registry source by `plugin_id`. 3. The backend installs using `download_url` when available; otherwise it installs using `repo`. 4. After installation, the backend reads the installed `metadata.yaml`. 5. The installed `metadata.yaml.author/name` MUST equal the selected `plugin_id`. 6. If identity validation fails, installation MUST fail. 7. A successful market install MAY persist `repo`, `download_url`, registry information, and install time in local state. ## 13. Update Rules For market-installed plugins: 1. Update checks MUST use the registry source recorded at installation time. 2. Update checks MUST resolve the market record by `plugin_id`. 3. Version comparison MUST use the installed metadata version and the market record `version`. 4. Update execution MUST use the resolved market record `repo/download_url`. For non-market installs: 1. URL installs, Git installs, and upload installs MUST NOT be treated as market installs. 2. They MAY persist `plugin_id` from installed `metadata.yaml`. 3. They MUST NOT enable market update checks unless explicitly bound to a market source. ## 14. Source Binding Rules When binding an installed non-market plugin to a market source: 1. The installed plugin identity MUST be read from installed `metadata.yaml`. 2. Candidate market records MUST have the same `plugin_id`. 3. Candidate market records SHOULD have the same repository identity. 4. Binding MUST persist the selected registry source and `plugin_id`. ## 15. Compatibility Consumers MAY read legacy market sources during migration. For legacy sources: - Consumers MAY accept sources without `$meta.schema_version` during migration. - Consumers SHOULD construct `plugin_id` from `author/name`. - Consumers SHOULD reject entries without `author` or `name` for market installation. - Consumers MAY use legacy root keys for display only. - Consumers MUST NOT use legacy root keys as plugin identity unless the key equals `${author}/${name}`. New market sources MUST include `$meta.schema_version`.