# Security and release

## What is isolated

The plugin runtime runs in a sandboxed Electron context with:

- `nodeIntegration: false` and `contextIsolation: true`;
- a CSP that denies network, object, frame, and worker access;
- a protocol handler that serves only files from the package;
- an isolated session partition;
- bounded JSON request/response limits;
- permission checks on every host operation.

The plugin receives no raw Electron API, Node `fs`, process secrets, arbitrary
browser DOM/session, cookie store, or local path to a selected import file.

## Minimum permissions

Do not declare every permission "just in case." It reduces user trust and
increases the impact of a defect. `plugin-reference` intentionally violates
this rule only as an API catalog.

For HTTP:

- use HTTPS only;
- declare hostnames without paths, credentials, or IP addresses;
- request only the required GET/POST operations;
- do not pass authorization or cookies manually when the auth broker is appropriate.

For browser capture:

- the host in `gameSources[].hosts` must be explicit;
- parse `html` as untrusted text;
- never place the complete page contents in diagnostics.

For backup/storage:

- persist only recoverable settings and domain data;
- exclude tokens, cookies, passkeys, magnets, torrent bytes, local paths, logs,
  temporary files, and cache;
- make migrations idempotent;
- honor `dryRun` during backup restore.

## Errors and diagnostics

Manifest errors are shown to developers with a path, code, message, and hint.
Runtime errors are stored in redacted diagnostics/run history. Keep `context`
to small scalar values:

```ts
await host.diagnostics.log({
  level: 'error',
  event: 'catalog.parse.failed',
  message: 'Provider response did not contain a game title',
  context: {status: 200, sourceId: 'catalog'},
});
```

Do not put a raw exception containing a filesystem path, response body, token,
or cookie into `message` or `context`. Messages containing such values are
hidden when displayed, but secrets must never be sent to diagnostics in the
first place.

## Versions and migrations

Increase the plugin `version` according to SemVer. Set `minAppVersion` only when
you use a newer application contract. When persisted data changes:

1. increase `contributions.settings.dataVersion` or `storage.dataVersion`;
2. implement the corresponding migration invocation;
3. preserve unknown fields when they remain compatible;
4. add tests for the old version and repeated migration execution;
5. update the backup data version when the backup schema changes.

Older `.xlplugin` versions remain installable only when their `minAppVersion` is
compatible. The application never silently overwrites an installed version.

## Package structure

```text
my-plugin.xlplugin
├── manifest.json
└── runtime/
    └── index.js
```

Build from a template:

```sh
pnpm dlx @xlibrary/plugin-sdk create my-plugin
cd my-plugin
pnpm install
pnpm test
pnpm demo
pnpm package
```

Before packaging, verify:

- the entry exists and ends in `.js`;
- the package contains no symlink, `..`, absolute path, or accidental secret;
- the runtime does not import the SDK as a normal runtime dependency;
- manifest permissions match the host calls made by the code;
- every returned object is JSON-safe and bounded;
- import/export/backup work in dry-run and failure cases.

## Publishing the SDK

The SDK is published separately from `.xlplugin` packages. To change the npm
package, first increase its `version`, then run these commands from
`packages/plugin-sdk`:

```sh
pnpm run type-check
pnpm run build
pnpm pack --dry-run
pnpm publish --access public --no-git-checks
```

npm versions are immutable: an already published number cannot be overwritten.
Publishing `@xlibrary/plugin-sdk` requires access to the `xlibrary` npm
organization and, when 2FA is enabled, a one-time authentication confirmation.
