Skip to content

Widget permissions

A custom widget starts with nothing. It gets a capability only if its widget.json declares it and the person allows it. What it can actually do is the overlap of the two.

Declare permissions in the permissions object of widget.json. Omit a key and the widget does not have it.

"permissions": {
"backend": false,
"node": [],
"network": true,
"networkHosts": [{ "host": "api.example.com", "methods": ["GET", "POST"] }],
"store": true,
"required": ["network"]
}
Manifest key Name shown to the person What it grants Can be switched off alone
backend (+ node) Run a backend Starts a sandboxed Node process. node is the allowlist of Node built-in modules it may require. No, only by disabling the widget
network + networkHosts Network access The backend may make requests to the declared hosts. Yes
networkAny Unrestricted network access Reach any host over https or wss. Separate from network; never implied by it. Yes
credentials Act as you at declared hosts Requests to a host are sent authenticated with a credential the person connected. The widget never sees the token. Yes
webAuth Authenticated web session Sign in to a website in an app window and fetch in that session. The cookie stays in the app. Yes
store Local storage The backend keeps its own data between restarts, on this machine (ctx.store). Yes
hubPositions Read hub positions Receives the live positions of other overlay widgets’ hub buttons. Yes
(none, implicit) Be clickable Registers the parts of the widget that can be clicked. The overlay ignores clicks anywhere else. Yes

Be clickable is held by every interactive widget without declaring it. A widget with "interactive": false does not get it.

network: true needs networkHosts. Each entry is a host ("api.example.com" or "*.cdn.example.com") or an object with host and methods.

  • No methods means GET only. Use "methods": ["*"] to allow any.
  • An empty networkHosts list denies every destination.
  • A bare "*" host is refused. Use networkAny for that, and only when you truly cannot name the destinations.
  • networkMode is "direct" (default; the request leaves from the widget’s frame, fenced to the declared hosts) or "proxy" (the request goes through the app).
"permissions": {
"network": true,
"networkHosts": ["api.github.com"],
"networkMode": "proxy",
"credentials": [{ "host": "api.github.com", "as": "github" }]
}

as names a credential the person has connected, never a secret. Nothing is attached until the person has also connected that credential and authorized this widget to use it. Use it with "networkMode": "proxy"; a direct request never passes through the app, so there is nothing to attach to.

webAuth is an object: loginUrl (the page the sign-in window opens), cookie (the cookie whose presence means signed in), domain, and fetchPrefixes (the URL prefixes the widget may fetch in that session).

List permissions the widget cannot run without in required. Everything is optional by default: a declined optional permission switches that feature off and the widget still runs, while a declined required one means the widget is not installed. You can only require what you declared.

  • Before first load. An untrusted widget triggers a permission review listing what it asked for, marking each line Required, Optional or Every widget (for the implicit one), and what happens if they refuse.
  • Allow. In Preferences, Plugins, a new widget shows Needs approval. Allow approves the whole list in one decision; Don’t install declines it and revokes the list. If the widget later asks for more, it shows Wants more access and Allow changes.
  • Revoke later. Single permissions can be switched off afterwards under Privacy & Security, while the widget stays enabled. Revoking takes effect straight away.

See Plugins and Custom widgets for the person’s view.