Request the least plugin permissions
Add a permission only when the plugin uses a supported Plugin API operation gated by it.
{
"permissions": [
"filesystem.read"
]
}An empty array is correct for a plugin that uses only always-available operations:
{
"permissions": []
}Map operations to permissions
| Permission | Request when the plugin must |
|---|---|
filesystem.read | list or watch folders, read text, or use another read-gated file operation |
filesystem.write | create, write, rename, or move files and folders to Trash |
clipboard.read | read the current system clipboard |
clipboard.write | replace current system clipboard content |
network.fetch | call the permission-gated api.fetch service |
workspace-folders.read | read supported Workspace Folder schemas, values, tags, ratings, or known folders |
workspace-folders.write | make validated atomic Workspace Folder mutations |
filesystem.write includes the supported read operations. Do not request both filesystem permissions. workspace-folders.write likewise includes workspace-folders.read; do not
request both Workspace Folder permissions.
Use Permission-gated Plugin API operations to look up the exact method-to-permission mapping.
Start from the workflow
List the plugin’s user-visible workflows, then map each operation:
| Workflow | Operations | Permission |
|---|---|---|
| Observe the next Phials save event | api.events.on | none |
| Show a notification | api.notify.success | none |
| Let a user choose a folder | api.files.pickDirectory | none |
| Read the chosen folder | api.files.readDirectory | filesystem.read |
| Save a review sidecar | api.files.writeText | filesystem.write |
This makes the permission explanation traceable to behavior. Do not request permissions for hypothetical future features.
Keep the list canonical
Use exact permission names once each:
{
"permissions": [
"clipboard.write",
"filesystem.read"
]
}Sort the list for stable diffs. Validation rejects unknown names, duplicates, and redundant filesystem combinations.
There is no catch-all permission and no shell.execute permission. Do not invent one or use raw host access as a substitute.
Understand permission review
Phials stores the exact permission set the user approved for the installed plugin ID.
When a later manifest changes that set:
- Phials marks the plugin as needing permission review.
- Its enabled preference remains recorded, but the changed release cannot activate.
- The Installed card shows the current requested permissions.
- The user approves or declines the complete new set.
- Only an approved, enabled plugin can activate.
Any set change can require review, including removing a permission. This prevents silent drift between the reviewed manifest and the installed one.
Changing plugin code without changing the permission set does not trigger another review.
Explain permission changes
When a release adds or changes permissions:
- name the user-visible feature that needs each permission
- explain when the operation occurs
- identify whether the action is user-initiated or automatic
- document how to avoid or disable the feature when applicable
Do not describe plugin permissions as an operating-system sandbox. Community plugins run as trusted renderer JavaScript; permissions gate supported Phials APIs. See Community plugin trust model.
Test the real manifest
In an isolated Phials Home:
- Install and approve a release with the old permission set.
- Enable and activate it.
- Install the release containing the changed set.
- Confirm Phials preserves the enabled preference, deactivates the old runtime, and shows permission review.
- Decline and confirm it remains disabled.
- Approve, enable, and exercise every gated operation.
- Remove the permission and verify the operation is rejected.
Test both permission presence and least privilege. A successful API call proves only the needed permission exists; it does not prove the manifest avoids unnecessary permissions.