Skip to content

Sandboxed and trusted ​

Extensions run in one of two tiers. Both get the same pages API, and Sorus checks every call against the same permissions.

Sandboxed: every extension ​

Each screen, view, block and job is a page in a sandboxed frame, served from the extension's own origin. Its content security policy allows:

  • scripts, styles, images, fonts and media from the extension's own files (plus inline styles, and data: and blob: images);
  • the SDK at /__pages/sdk.js.

And nothing else: no inline scripts, no network (connect-src 'none'), no frames, no form submissions. The only way out is the SDK, over a message channel Sorus hands the frame when it loads.

Trusted: signed by Sorus ​

An extension whose files carry Sorus's signature can go further:

  • A screen, view or block with a module runs that module in Sorus's own window instead of its entry frame. Sorus imports it and calls its mount(root, pages), drawing into a shadow root, so its styles stay its own while the theme's variables reach it.
  • An app may sit among Sorus's own places, with one of Sorus's icons.

Today and Calendar are built this way, as is Basics' Board view.

js
// view.js, named as the view's `module`
export function mount(root, pages) {
  const board = document.createElement('main')
  root.append(board)
  const stop = new AbortController()
  void (async () => {
    for await (const _ of pages.client.changes({ signal: stop.signal })) draw()
  })()
  draw()
  return () => stop.abort()
}

mount may return a function, called when Sorus takes the view away.

Keep the entry page working too: Sorus uses it whenever the extension isn't signed, such as when you load its folder to develop it. Basics' board.html loads board.js, which connects and calls the same mount.

Only Sorus can sign extensions; the keys it trusts are built into the app. See Packaging and signing.