# Manifest

The `manifest.json` file is located at the root of the `.xlplugin` archive. The
application validates the archive and manifest first, then shows permissions to
the user and starts the runtime only after explicit approval.

## Basic example

```json
{
  "format": "xlibrary-plugin",
  "manifestVersion": 2,
  "apiVersion": 1,
  "id": "my-game-catalog",
  "name": "My Game Catalog",
  "version": "1.0.0",
  "minAppVersion": "2.0.8",
  "description": "Searches games from My Game Catalog.",
  "entry": "runtime/index.js",
  "permissions": ["game-sources.providers", "network.http"],
  "contributions": {
    "network": {"allowedHosts": ["catalog.example"]},
    "gameSources": [
      {
        "id": "catalog",
        "title": "My Game Catalog",
        "description": "Search and resolve games.",
        "hosts": ["catalog.example"],
        "operations": ["search", "resolve"]
      }
    ]
  }
}
```

The required values are `format`, `manifestVersion`, `apiVersion`, `id`,
`name`, `version`, `entry`, `permissions`, and `contributions` (the last two
may be empty). `id` and all local contribution ids use lowercase kebab-case.
`version` and `minAppVersion` use strict SemVer. `entry` must be a safe relative
path ending in `.js`; traversal, absolute paths, symlinks, and other unsafe
entries are rejected.

## Permissions

Request the smallest possible set. A permission is a capability, not a comment:
the host checks it on every operation.

| Permission | Grants |
| --- | --- |
| `library.games.read` | Read normalized games through `host.games.list` and receive full game contexts in UI, filter, and action invocations. |
| `library.games.write` | Apply the `gamePatch` returned by a game action. |
| `sessions.events.read` | Read sessions and receive tracking session events. |
| `tracking.providers` | Register tracking contributions. Tracking events also require `sessions.events.read`, `library.games.read`, and `game.custom-fields`. |
| `game.custom-fields` | Declare fields, read their values, and write values from tracking/actions. |
| `game.actions` | Add actions to game surfaces. |
| `plugin.storage` | Persistent plugin-owned JSON storage and storage migrations. |
| `plugin.cache` | A disposable TTL cache. |
| `plugin.settings` | Settings schema, settings reads/writes, and settings migrations. |
| `plugin.auth` | Auth broker login flow, status, clear, and requests without exposing cookies or tokens directly to the plugin. |
| `network.http` | HTTPS GET/POST requests to hosts in `contributions.network.allowedHosts` that the user approves when enabling the plugin. |
| `notifications.show` | Small host notifications subject to a rate limit. |
| `jobs.run` | Jobs triggered by startup, session-started, or session-ended. |
| `game-sources.providers` | Add a provider to `+ Add game` with standard search/resolve/refresh operations. |
| `browser.page.capture` | Process data-only page captures from the browser extension. |
| `imports.sources` | Add a source to universal Import and read the selected file in chunks. |
| `exports.targets` | Add a target to universal Export and write the result in chunks. |
| `backup.contribute` | Add a versioned section to backups and restore it. |
| `filters.facets` | Add typed facets and compute matching game ids. This also requires `library.games.read`. |
| `ui.game-details.sidebar` | Add a data-only UI contribution to the game details sidebar. |
| `ui.game-details.header` | Add a data-only UI contribution to the game details header. |
| `ui.dashboard.widget` | Add a data-only dashboard widget. |
| `ui.library.toolbar` | Add data-only actions/panels to the library toolbar. |
| `ui.settings.section` | Add a data-only section to Settings. |

The `ui.*` permissions are also the allowed values for
`contributions.ui[].slot`. The application rejects a UI contribution without
the matching permission.

## Contributions

Each group describes where the plugin appears in the standard UI or lifecycle:

| Group | Main fields | Runtime method |
| --- | --- | --- |
| `imports` | `id`, `title`, `description`, extensions, automatic detection | `imports.parse` |
| `exports` | `id`, `title`, `description`, extension, MIME | `exports.serialize` |
| `filters` | `kind`, `id`, options or range | `filters.evaluate` |
| `ui` | `id`, `slot`, `title`, description | `ui.render`, `ui.action` |
| `gameFields` | `id`, `kind`, label, options/range | The host stores values; actions/tracking return values |
| `tracking` | `id`, title, event types | `tracking.session-event` |
| `gameActions` | `id`, title, confirmation flag | `game-actions.execute` |
| `settings` | data version and typed fields | `settings.migrate` |
| `storage` | data version | `storage.migrate` |
| `network` | allowlisted hostnames | `host.network.request` |
| `auth` | login/completion URLs and allowlisted hosts | `host.auth.*` |
| `jobs` | triggers and minimum interval | `jobs.run` |
| `gameSources` | hosts and operations | `game-sources.*` |
| `backup` | data version and title | `backup.capture`, `backup.restore` |

All ids must be unique within a group. XLibrary adds an owner and namespace to
external ids, so plugins should not attempt to create global ids manually.

## Localized metadata

`locales` contains BCP-47 keys (`en`, `ru`, `en-US`), not separate runtime
plugins. It can localize the plugin name, description, labels, and contribution
metadata:

```json
{
  "locales": {
    "ru": {
      "name": "Game Catalog",
      "description": "Search games in the catalog.",
      "contributions": {
        "gameSources": {
          "catalog": {
            "title": "Catalog",
            "labels": {"search": "Search"}
          }
        }
      }
    }
  }
}
```

Missing fields fall back to the base manifest. Localization does not change
ids, permissions, hosts, or runtime behavior.
