Skip to content

FluxDown 0.5.0 ships the native GPUI client — a note from the author on why the whole interface was rebuilt.Take a look

FluxDown
Download Docs Pricing Changelog Sponsor
Plugin Development Permissions, Consent & Audit

Plugin Development

Permissions, Consent & Audit

What each plugin permission grants, how risk levels and consent work (UI and headless), how the market checks packages, what is audited, and what full trust means.

Edit this page

Plugins are not sandboxed to the point of uselessness: a plugin may ask for powerful capabilities. FluxDown’s rule is that every capability is declared in the manifest, shown to you and approved before it works, and recorded in an audit log.

This page covers the user-facing model and the rules a plugin author needs to know. The manifest fields themselves are in the Manifest reference; the APIs behind each permission are in the API reference.

Permissions

PermissionGrants
(none)The base sandbox: flux.fetch, flux.storage, scratch files, logging, view / command contributions.
ffmpegflux.ffmpeg / flux.ffprobe on finished files.
ytdlpflux.ytdlp.
authPlatform login flows and stored credentials used by flux.fetch / flux.auth.
tasksflux.tasks.*: list, create, pause, resume and delete download tasks.
browserRulesContribute rules to the browser extension (contributes.sniffRules).
backgroundKeep the main script running with the daemon.
interceptInterceptors: read and change the URL, request headers, cookies and body of matching tasks, control their connection count, skip the probe, swap credentials mid-download, rewrite or filter streaming segments, choose the quality or DASH Period, process the file before completion, and ask for a retry after a task fails. See the Interceptors guide.
desktopRun native desktop scripts with full trust (see below).

Subscription providers, hooks, resolvers and portable views need no permission of their own.

Risk levels

The risk level is computed from the permissions — it is not self-declared.

LevelWhenShown as
StandardNo permissions.—
ElevatedAny of ffmpeg, ytdlp, auth, tasks, browserRules, background, intercept.Warning badge
Fulldesktop.Destructive badge

The badge appears on installed plugins and on market listings.

A plugin runs only with the capabilities you have approved. The approval is stored per plugin as the exact set of permissions and network hosts you saw, together with the version.

  • Install or update. When a plugin is installed (market, .fxplug, directory or dev install) or updated, and its permissions or its desktop.networkHosts are not covered by what you approved before, it is installed but stays disabled with the reason consent required until you approve. In the desktop app a consent dialog shows the risk badge, each permission with what it allows, and the list of network hosts, and the plugin’s row gets a Grant button.
  • Full trust. For a Full plugin the dialog adds an explicit warning, and the confirm button is enabled only after you tick I understand.
  • Market installs. The confirmation shown before a market install is the same dialog; confirming it there grants the same set, so you are not asked twice. The dialog records the version it showed; if the market’s latest version has changed in the meantime, the install is refused so you can review the new version’s permissions first.
  • Updates ask again only for what is new. A new version that adds a permission or a network host needs your approval; one that does not changes nothing.
  • Upgrading FluxDown. Plugins installed before consent existed keep working: the permissions they had at that point are recorded as approved (marked as a migration in the audit log). Anything new they request later needs approval, and plugins you install afterwards are never pre-approved.
  • Dev installs are not exempt. A dev-mode plugin that declares permissions also waits for approval before it runs.

Approving is exact: the approved set must match what the plugin currently requests, no more and no fewer.

On a headless server, or in any setup without the desktop dialog, approve through the service RPC. Call daemon.plugin.consent on the agent/daemon /rpc endpoint (see the API overview) with the plugin’s identity and the complete lists it currently requests:

{
  "identity": "acme@demo",
  "permissions": ["background", "tasks"],
  "networkHosts": []
}
  • permissions is the plugin’s whole permissions array; networkHosts is the union of every view’s desktop.networkHosts. Order and duplicates do not matter; hosts are compared exactly (case-sensitive).
  • If either list differs from what the manifest requests, the call fails with consent does not match requested capabilities. Read the plugin’s current request from daemon.plugin.list (consentPending, consentPendingHosts) first.
  • Until consent is complete, enabling the plugin (daemon.plugin.setEnabled with true, or PUT /api/v1/plugins/{identity}/enabled) fails with consent required.

After a successful call the plugin loads and enables normally. The grant is written to the audit log as consent.granted.

How the market checks a package

The plugin index entry carries the permissions and network hosts that the market UI shows you before installing. FluxDown re-checks the downloaded package against that entry before installing it:

  • the package manifest’s identity and version must equal the index entry’s;
  • every permission and every network host in the package manifest must be contained in the index entry’s lists — a package that asks for more than the entry advertised is refused. (Contained, not equal: that is the market’s anti-escalation check. The consent check above is stricter and requires an exact match.)

The index itself is checked automatically on every release. The first registration of a plugin is reviewed by a maintainer; later releases of an already registered plugin are not reviewed by hand — they pass or fail the automatic admission checks. Permissions are always shown to you before you install. See Packaging & Plugin Market.

Full trust: desktop scripts

A view’s desktop script runs inside the desktop app process, as an ES module under the GPUI Shell. It is not sandboxed with respect to your machine: it can read and write files, start processes and use the clipboard with your user’s privileges.

Its network access is the exception. networkHosts is an enforced allow-list: the script can connect only to the exact hostnames listed (no wildcards), and the list is shown in the consent dialog. Hosts not listed are refused. File system, process and clipboard access are not restricted, and process.exit is never granted.

Install a plugin with desktop only if you trust its author the way you would trust any program you download and run. For the author’s side — how to write these scripts and what the module offers — see the Desktop UI guide.

Native file, process and network operations performed by desktop scripts are not logged one by one. The audit log records that a desktop script was loaded, unloaded, and every fluxdown.call / fluxdown.invoke it made — the coarse boundary where it touched FluxDown — but nothing about what it did on its own with the operating system.

Desktop scripts do not run in the daemon, on Web or on mobile.

Failure circuit breaker

A plugin whose calls time out or exceed their memory limit three times in a row is disabled automatically (the audit log records breaker). Installing a new version of the plugin clears the tripped state. See Debugging for how to tell a tripped breaker from other disabled states.

Audit log

Open Settings → Extensions → Plugins → Audit log, or Audit record in a plugin’s details for that plugin only. Each row has a time, an origin (daemon or desktop), an action, a target, an outcome (ok, denied, error) and detail. You can filter by plugin, load older rows, copy the log as JSON, and clear it (with confirmation).

The same data is available over RPC: daemon.plugin.audit.list (paged, optional identity filter) and daemon.plugin.audit.clear.

ActionWhen
install, update, uninstall, enable, disableLifecycle changes.
consent.granted, consent.migratedYou approved capabilities; or the upgrade recorded existing ones.
revokedThe market revoked the plugin.
breakerThe plugin was disabled by the failure circuit breaker.
fetchflux.fetch; the target is METHOD scheme://host/path — the query string and credentials are removed — and the detail is the status.
auth.save, auth.removeCredential changes.
fs.write, fs.removeWrites to the plugin’s scratch workspace.
ffmpeg.run, ffprobe.run, ytdlp.runTool runs; the target is the argument list (shortened).
task.retry, task.artifactTask requests from hooks.
intercept.<stage>An interceptor call for that stage (prepare, segments, finalize, …); the target is the task id, the detail is empty or the error text.
tasks.create, tasks.controlflux.tasks calls from main. Task creation is rate limited; a refused call is recorded with outcome denied.
settings.updateflux.settings.update.
desktop.load, desktop.unloadA desktop script started / stopped; the detail lists the grants it ran with.
desktop.call, desktop.invokeRPCs made from a desktop script.
audit.clearThe log was cleared.

Details:

  • A row’s target is cut to 512 bytes and its detail to 2048 bytes.
  • desktop.* rows are reported by the desktop client through daemon.plugin.audit.record; the origin is always forced to desktop, at most 100 rows per call.
  • Reads, flux.storage and logging are not recorded. Rows older than 90 days are removed, and the log keeps at most the latest 20,000 rows. Nothing is written while plugins are idle.

Something wrong with this page?

Pages
Actions
↑↓to navigateto open