Phials developer documentation
User guide

Community plugins

Community plugins are JavaScript bundles Phials loads from disk: manifest.json, main.js, and optional styles.css. The host installs artifacts under the portable Phials data directory, resolves updates from a community index and GitHub Releases, and activates your code with import() of main.js. Your default export must be a function that returns a PhialsPlugin.

The host wraps PluginAPI with manifest-derived permission gates (scoped invoke, optional clipboard / fetch). This is trusted execution in the renderer with permission-gated Phials APIs — not a full sandbox. See Public API contract.

Companion: Plugin API · Getting started.


Community plugins safe mode

Safe mode is on by default for new profiles. While enabled:

  • Browsing the community index, installing, enabling, updating (from the index), and refreshing the index are blocked.
  • Already-enabled community plugins are disabled when safe mode is on (for example after the app restarts or when the user turns safe mode back on).

Users must open Settings → Plugins → Community plugins, turn safe mode off, and accept the warning before they can discover or install community plugins. Features that ship with Phials (not community bundles) are unaffected. For testers and supporters, see Community plugins in Phials.


Manifest (PluginManifest)

Parsing and validation helpers live in the example repo as sdk/manifest-schema.ts. Required fields:

  • id — pattern ^[a-z][a-z0-9]*\.[a-z][a-z0-9-]*[a-z0-9]$ (e.g. acme.preview-plugin). Cannot collide with reserved phials.* ids.
  • name, version, minAppVersion, author, description — semver rules apply to plugin and minimum app versions.
  • authorUrl, repository, icons, permissions, pluginApiVersion optional (pluginApiVersion defaults to 1.0.0 when omitted in compatibility checks).

permissions must be subsets of the supported union:

filesystem.read | filesystem.write |
clipboard.read | clipboard.write |
network.fetch

shell.execute is not a supported manifest permission in v1.

Use PERMISSION_DESCRIPTIONS / PERMISSION_RISK from sdk/manifest-schema.ts for UI copy or tooling.

Minimal manifest:

{
  "id": "acme.hello-plugin",
  "name": "Hello Plugin",
  "version": "1.0.0",
  "minAppVersion": "0.1.0",
  "pluginApiVersion": "1.0.0",
  "author": "You",
  "description": "Example external plugin bundle.",
  "permissions": ["filesystem.read", "network.fetch"]
}

Compatibility

The host checks:

  • minAppVersion against the running Phials application version.
  • pluginApiVersion (default 1.0.0) against the host supported plugin API version.

Both must be satisfied before install or activation succeeds. See Public API contract.


Loader flow (behavioral)

Initialize

On startup the host loads installed plugin manifests, merges enabled flags from persisted state, applies safe mode (disabling community plugins when required), then activates each enabled plugin.

Install

Resolution from the community index → download manifest.json, main.js, optional styles.css → validate id, compatibility, and permissions shape → stage files on disk → finalize after successful activation or roll back on failure. New installs start disabled until the user enables them.

Enable / disable

Enable activates the plugin (blob import, register, onActivate). Disable tears down providers, injected styles, shortcuts, and event subscriptions for that plugin.

Updates

Updates reinstall files. If manifest permissions change compared to the last user-approved set, the host requires permission review before re-enabling; failed activation after an update can roll back to the previous on-disk version.

Activation

The host reads main.js, imports it, expects a default export function that yields a PhialsPlugin, checks plugin.id matches the install id, wires the permission-gated PluginAPI, then activates. Optional styles.css is injected with a plugin-scoped style element; it is removed on disable/uninstall.


Permission-gated PluginAPI

invoke

Allowed command names are derived from declared permissions plus a small always-allowed baseline (system paths, drives, thumbnails, platform probes). filesystem.read adds read-oriented file/directory commands; filesystem.write adds mutating file commands (and implies the read-side set for the API layer). Anything else throws a permission error.

clipboard

clipboard.read / clipboard.write gate the corresponding PluginAPI.clipboard helpers when present.

fetch

network.fetch gates PluginAPI.fetch when present. It does not prevent a malicious bundle from calling the browser’s global fetch directly — see the contract.


Trust model (v1)

Distribution assumes GitHub release trust plus registry review — not mandatory signatures or checksums in v1. See Public API contract.


Submitting to the community registry

The canonical index is the community-plugins.json file in github.com/EliWimmer/phials-plugins (served to Phials via raw GitHub). Maintainer checklist: SUBMISSION.md. Takedowns, broken releases, and ownership: POLICY.md.

Phials validates index shape at load time (unknown fields and invalid entries cause the index refresh to fail with a clear error).


Limits today

  • Distribution is geared toward GitHub Releases (and the community index your Phials build uses). Other channels are not documented here.
  • Plugins run in the same renderer as Phials; stronger isolation is not part of the current model (see the contract).