Permissions Reference

StudioBrain plugins declare the capabilities they need in plugin.json. The host runtime enforces these declarations, granting access only to what the plugin has declared and the user has approved. This page is the complete reference for the capability system.

This page covers runtime capability/consent gating — what host functions a plugin can call once it’s installed. As of SBAI-4630/SBAI-4641 there is a separate, earlier gate: trust tiers, checked at install time before the capability system ever runs. See Trust Tiers below for how tiering and capability consent interact.

Trust Tiers (SBAI-4630)

Every plugin belongs to one of four trust tiers, declared as trust_tier in plugin.json and verified against a code signature at install time — not just self-declared:

TierSigned?Who
first_partyYes, Biloxi Studios keyPlugins Biloxi Studios publishes itself
trusted_vendorYes, vendor keyLarge, vetted vendors (Adobe/Google-scale)
partnerYes, partner keyVerified studios and contributors
communityNoEveryone else

Verification is Ed25519 signature-based, not marketplace review or self-declaration alone. Three values must agree before a plugin is trusted at a given tier: the manifest’s declared trust_tier, the signature’s claimed tier, and the tier recorded against the matching public key in the trusted-key registry (resources/plugin-trusted-keys.json on the host, overridable via SB_PLUGIN_TRUSTED_KEYS_FILE). A community-tier plugin is, by definition, unsigned — it is never marked is_verified, regardless of what its manifest claims.

Enforcement point: install-time manifest/signature validation in sb-server (core/crates/sb-server/src/routes/plugins.rs, services/plugin_signature.rs), not the wasmtime runtime. The runtime’s host-function gating (everything in the rest of this page) is unchanged and unaware of trust tiers — tiering decides whether a plugin gets a verified badge and whether consent is forced; the capability system still decides what host functions it can call once running.

Interaction with consent (SBAI-4641): community-tier plugins always require an explicit consent dialog on install, even if they declare zero non-auto-granted capabilities — the tier itself is the thing the user is consenting to, not just the capability list. first_party, trusted_vendor, and partner plugins still go through the same capability-based consent dialog described below for anything beyond the auto-granted set; a higher trust tier does not bypass capability consent, it only establishes the verified-signature badge users see before they decide.

Capability Matrix

CapabilityDescriptionAuto-grantedDesktopCoreCloud
entity_readRead entity markdown and metadataYesYesYesYes
asset_readRead entity assets (images, files)YesYesYesYes
ai_generateUse the AI generation serviceYesYesYesYes
entity_writeCreate, modify, or delete entitiesNoConsentConsentConsent
asset_writeUpload, modify, or delete assetsNoConsentConsentConsent
http_requestMake outbound HTTP requestsNoConsent (any domain)Consent (any domain)Not exposed (host callback has no HTTP action)
file_readRead files under the plugin’s own storage root (_PluginData/{plugin_id}/), relative paths only; host check is the native read_data permissionNoConsentConsentBLOCKED
file_writeWrite or delete files under the same plugin storage root; host check is the native write_data permissionNoConsentConsentBLOCKED

Auto-granted capabilities

These capabilities are enabled automatically when a plugin is installed. They are considered safe because they are read-only or usage-tracked:

  • entity_read — The plugin can read entity frontmatter and markdown content. It cannot modify anything.
  • asset_read — The plugin can read files attached to entities (images, documents, audio). Read-only.
  • ai_generate — The plugin can call the AI generation service. Usage is metered through your BrainBits balance, so the plugin cannot generate unbounded costs.

These capabilities require the user to approve a consent dialog before the plugin can use them:

  • entity_write — The plugin can create new entities, modify existing entity fields and content, and delete entities. All changes appear in version history.
  • asset_write — The plugin can upload new assets, replace existing assets, and delete assets from entities.
  • http_request — The plugin can make outbound HTTP requests. See Domain Restrictions for Cloud-specific behavior.

Platform-restricted capabilities

These capabilities are blocked on certain platforms regardless of user consent:

  • http_request — Make outbound HTTP requests from the plugin. Not exposed on Cloud: the official-plugin host callback (packages/cloudflare-worker/sb-server-worker/src/plugin_host.rs) dispatches seven content, settings and log actions and has no HTTP action, so a Cloud plugin has no host path for http_request, and capabilities.http_domains is not read or enforced there. This describes the host-callback boundary only; it is not a statement about Cloudflare Workers’ own outbound fetch or about deployment egress policy. Available on Desktop and Core with consent.
  • file_read — Read files by path relative to the plugin’s storage root ({content_base}/_PluginData/{plugin_id}/), never the host filesystem; the WASM host gates it on the native read_data permission of the host-storage interface in core’s packages/plugin-sdk/wit/plugin.wit. Available on Desktop and Core with consent. Blocked on Cloud because Cloud runs in a server environment where plugin-scoped file storage is not meaningful.
  • file_write — Write or delete files under the same plugin storage root, gated on the native write_data permission. Same platform restrictions as file_read.
🚫

Plugins that require file_read or file_write cannot declare cloud in their platforms field. The marketplace CI validation will reject submissions that declare both Cloud support and filesystem capabilities.

Domain Restrictions

The http_request capability works differently across platforms:

Cloud (not exposed by the host callback)

On Cloud, the official-plugin host callback exposes no HTTP action: its seven actions are content, settings and log operations (packages/cloudflare-worker/sb-server-worker/src/plugin_host.rs), so http_request has no host path and the Cloud host neither reads nor enforces capabilities.http_domains. Declaring http_domains has no effect on Cloud. This is the boundary of the plugin host API, not a claim about Worker egress and not a service-binding workaround.

Desktop and Core (Informational)

On Desktop and Core, the declared domains are shown in the consent dialog for transparency, but they are not enforced. After the user approves the http_request capability, the plugin can reach any domain.

Even on Desktop and Core, declaring your actual domains in http_domains is good practice. It lets users make an informed decision during the consent dialog.

Capability Details

entity_read

What it grants: Read access to entity data through host functions.

Host functions enabled:

  • entity_read(entity_type, entity_id) — Read a single entity
  • entity_list(query) — List entities with filters

What the plugin sees:

  • Entity frontmatter (all YAML fields)
  • Entity markdown body
  • Entity metadata (created_at, updated_at, entity_type, entity_id)

What the plugin cannot do:

  • Modify any entity data
  • Access entities outside the current project scope (on StudioBrain Cloud, multi-tenant isolation is enforced by the host)

asset_read

What it grants: Read access to entity assets.

Host functions enabled:

  • asset_read(entity_type, entity_id, filename) — Read a single asset file

What the plugin sees:

  • Binary content of the asset file
  • Asset filename and content type

What the plugin cannot do:

  • Modify or delete assets
  • Access assets from entities it cannot read (respects entity_read scope)

ai_generate

What it grants: Access to the StudioBrain AI generation service.

Host functions enabled:

  • ai_generate(prompt, model, options) — Generate text content

Billing:

  • Each generation call consumes BrainBits from the user’s account
  • The plugin cannot exceed the user’s available balance
  • Usage is attributed to the plugin in the billing dashboard

Rate limits:

  • Same rate limits as the user’s direct AI usage
  • Shared quota (plugin calls count toward the user’s limits)

entity_write

What it grants: Write access to entities.

Host functions enabled:

  • entity_create(entity_type, data) — Create a new entity
  • entity_update(entity_type, entity_id, content) — Update an existing entity
  • entity_delete(entity_type, entity_id) — Delete an entity

Audit trail:

  • All write operations are logged in the entity’s version history
  • The log entry shows the plugin ID as the author (e.g., “Modified by plugin: jira-sync”)

Scope:

  • Write access applies to all entity types the plugin has access to
  • On StudioBrain Cloud, tenant isolation is enforced (a plugin cannot write to another tenant’s entities)

asset_write

What it grants: Write access to entity assets.

Host functions enabled:

  • asset_write(entity_type, entity_id, filename, data) — Upload or replace an asset

Constraints:

  • Same size limits as manual uploads (configurable per edition)
  • Asset type restrictions apply (configured in entity templates)

http_request

What it grants: Ability to make outbound HTTP requests from the WASM sandbox.

Host functions enabled:

  • http_request(url, method, headers, body) — Make an HTTP request

Platform availability:

  • Cloud: not exposed. The official-plugin host callback has no HTTP action (seven content/settings/log actions only), so there is no host path for http_request and http_domains is not enforced. Host-callback boundary only; not a Worker egress statement.
  • Desktop and Core: governed by those hosts’ own gates, which this Cloud correction does not describe. On Core main the runtime requires the net_http capability plus network.allowed_domains and caller consent, and builds a deny-all HTTP client unless hosts are granted (crates/sb-plugin-wasmtime/src/runtime.rs, host_state.rs); the Desktop/Core sections of this page are reconciled under SBAI-13559.

Request limits:

  • 30 requests per minute per plugin instance
  • 5 second timeout per request
  • 1 MB maximum response body

file_read

What it grants: Read access to files under the plugin’s own storage root ({content_base}/_PluginData/{plugin_id}/), addressed by paths relative to that root. The WASM host enforces it through the read_data permission on the host-storage interface (read-file, list-files; core packages/plugin-sdk/wit/plugin.wit, host-storage section). file_read is this page’s capability name; it is not itself a host check or an installer permission.

Host functions enabled:

  • file_read(path) — Read the file at path, relative to the plugin’s storage root (host-storage read-file; list-files enumerates that root)

Platform availability:

PlatformAvailableNotes
CloudNoBlocked. Returns capability_blocked error.
DesktopYesConsent required. WASM plugins operate within a plugin-scoped directory under the project content root ({base}/_PluginData/{plugin_id}/).
CoreYesSame as Desktop. Core’s wasmtime host has no native Python or Lua engine.
⚠️

Filesystem access is plugin-scoped storage, not general host filesystem access. The WASM host creates a dedicated directory for each plugin at {content_base}/_PluginData/{plugin_id}/ and enforces two restrictions: (1) absolute paths are rejected, and (2) path traversal (.. components) is blocked. Plugins can read/write any relative path within their own scoped directory — there is no manifest-based path allowlist. The contract is the host-storage interface in core’s packages/plugin-sdk/wit/plugin.wit: read-file and list-files require the native read_data permission, write-file and delete-file require write_data; the filesystem:read / filesystem:write spellings seen in older tickets are not host checks or installer permissions. Native Python is not loaded on Desktop, Core, Cloud, or mobile. Lua 5.4 runs only in the sandboxed mobile client host (sb-client-plugins), which removes dangerous builtins (io, os, debug, require, loadfile, dofile, load). Only install plugins from trusted authors.


file_write

What it grants: Write and delete access under the same plugin storage root, by relative path, enforced through the write_data permission on the host-storage interface (write-file, delete-file).

Host functions enabled:

  • file_write(path, data) — Write data to path, relative to the plugin’s storage root (host-storage write-file; delete-file removes a file there)

Platform availability: Same as file_read.

Safety:

  • WASM plugins operate within a plugin-scoped directory ({content_base}/_PluginData/{plugin_id}/), with absolute paths and path traversal (..) blocked at the sanitizer layer
  • Native Python is not executed, so it has no filesystem access
  • Lua runs only in the sandboxed mobile client host and does not have unrestricted filesystem access
  • No automatic backups are made before writes — plugins are responsible for safe writes

When a plugin is installed (or updated with new capabilities), StudioBrain shows a consent dialog for any non-auto-granted capabilities.

What the dialog shows

Plugin "My Plugin" requests the following permissions:

  [auto]  entity_read     Read your entity data
  [auto]  asset_read      Read your entity assets
  [!]     entity_write    Create and modify your entities
  [!]     http_request    Make network requests to:
                          api.example.com, cdn.example.org

  [Enable]  [Cancel]
  • [auto] items are informational. They are granted regardless of the user’s choice.
  • [!] items require explicit approval. If the user clicks Cancel, the plugin is not installed.
  • For http_request, the declared domains are listed.

Partial approval

The current consent model is all-or-nothing for consent-required capabilities. If a plugin requests entity_write and http_request, you must approve both or cancel the installation entirely.

After installation, you can revoke individual capabilities in Settings > Plugins > [Plugin] > Permissions. The plugin may not function correctly without all its declared capabilities.

If a plugin update adds new consent-required capabilities, the consent dialog appears again showing only the new capabilities. Previously approved capabilities are not shown again.

Storage and Config Capabilities

The following host functions are always available to any plugin, regardless of declared capabilities:

FunctionPurpose
storage_get(key)Read plugin-scoped persistent data
storage_set(key, value)Write plugin-scoped persistent data
storage_delete(key)Delete plugin-scoped data
storage_list(prefix)List keys in plugin storage
get_config(key)Read a plugin setting value
set_config(key, value)Write a plugin setting value
log(level, message)Write to the plugin log
ui_notify(title, message, level)Show a toast notification

These functions operate within the plugin’s own scope. A plugin cannot read another plugin’s storage or settings.

Security Considerations

WASM sandbox guarantees

  • Plugins cannot access host memory outside their linear memory.
  • Plugins cannot call host functions they have not declared.
  • Plugins cannot bypass domain restrictions on Cloud.
  • Plugins cannot access other plugins’ data or storage.

What the sandbox does NOT protect against

  • A plugin with entity_write can modify any entity the user has access to.
  • A plugin with http_request can exfiltrate entity data to declared domains.
  • A plugin with ai_generate can consume BrainBits.
  • Native Python is not executed, so it cannot bypass the sandbox.
  • Lua does not bypass the sandbox. It runs only in the mobile client host, with io, os, debug, require, loadfile, dofile, and load removed.

Recommendations

  • Install only plugins from authors you trust.
  • Review the declared capabilities before approving the consent dialog.
  • Prefer a WIT Component. Native Python is not loaded. Lua is sandboxed and is not a Desktop or Core runtime.
  • Revoke capabilities you do not need after installation.
  • Monitor plugin activity in Settings > Plugins > [Plugin] > Activity Log.

Next Steps