Venora v0.1.0 · in development

Security & privacy

Less trust, by design.

Venora is designed to reduce the trust and privilege a plugin needs, rather than treating every plugin as fully trusted code. This page explains how it does that, what it stores, and where its protections stop.

  • Written requirements: SEC-1 to SEC-13
  • Tested in Chromium
  • No independent audit published

01 Trust model

Who trusts what.

The trust boundary is each plugin's sandboxed frame and its message channel. Inside, the plugin controls everything. Outside, Venora validates everything that crosses.

Venora is trusted code

When you install the extension, you trust it. It runs inside Fermo's page and can read that page and Fermo's local account data, which it needs in order to guard your token and to do work on plugins' behalf.

Plugins are untrusted

Imported plugin code and manifests are treated as untrusted. Bundled plugins get no exceptions: they run in the same sandbox, with only the permissions they declare.

The page isn't trusted with the channel

Plugin frames sit inside a closed shadow root, and each private channel is handed over once with a random nonce. Fermo's own scripts cannot obtain a plugin's channel.

02 Architecture

Follow a plugin outward.

Each layer removes something a plugin could otherwise reach. Scroll through them in order, from the plugin's own code out to the browser toolbar.

  1. 01

    The plugin runs in a dedicated worker

    Plugin code has no window, document, parent or assignable location. It cannot touch any DOM, and it cannot navigate a frame to smuggle data out in a URL.

    It gets the standard JavaScript built-ins, timers, console and a few web-platform basics. eval and new Function are blocked. The code is a single ES module loaded from a data: URL.

  2. 02

    Inside a sandboxed extension frame

    The worker is created by a sandboxed extension page with an opaque origin and no chrome.* APIs. Its content security policy is default-src 'none', so fetch, XHR, WebSocket and loading images, scripts or frames are blocked. The worker inherits this policy, which the browser test confirms in Chromium.

    An opaque origin also means no access to Fermo's localStorage, IndexedDB or cookies, which is where Fermo keeps your session.

  3. 03

    One private channel per plugin

    Frames sit in a closed shadow root. At start, Venora posts a connect message with a random 128-bit nonce and one transferred MessagePort. The sandbox accepts exactly one, and only from its parent.

    Venora decides which plugin is asking from the port a request arrives on. A request that includes a plugin ID is rejected. Only plain, JSON-compatible data can cross, within size limits.

  4. 04

    Every privileged operation is a capability

    The registry is fixed at load. The dispatcher checks that the capability exists, that every permission it needs was declared, and that the payload is valid, in that order. Permissions are copied from the validated install record when the plugin starts, so nothing the plugin sends can change them.

    Internal exceptions come back only as a generic capability_failed. Stack traces, paths and server responses are never passed to the plugin.

  5. 05

    The session token is never sent

    Capabilities that need your session use it inside Venora and return only a derived result. Then, as a backstop, every message to a plugin passes through one function that checks it against your current tokens, for every Fermo account in the profile, including inside longer strings.

    A response containing a token becomes sensitive_data_blocked. An event containing one is dropped. If the tokens can't be read, nothing is sent. The guard never logs the token.

  6. 06

    Each plugin has its own resource account

    Requests waiting for an answer, request rate, each costly capability's rate and event rate are all counted per plugin. Over a limit, requests are refused with rate_limited before any work is done.

    A watchdog pings each worker every 2 seconds and stops one that hasn't answered within 5, removing its frame and worker.

  7. 07

    Venora's own UI is out of reach

    Dialogs, notices and the Plugins page render in shadow roots on hosts with inline !important styles, and dialogs and notices use the browser's top layer. Plugin CSS cannot restyle, hide or cover them.

    Every plugin-provided string is rendered as text, never HTML. Imported plugins' notices carry a fixed “Plugin” badge and the plugin's name, so a plugin calling itself “Venora” still can't pose as Venora.

  8. 08

    Only on Fermo's own page

    Venora runs only in Fermo's top-level page at fermo.sbar.fyi. It never runs in a framed copy of Fermo or on a look-alike origin. Plugins run on /channels/ pages, and the page context is checked again before every start, request and event.

    If Fermo leaves /channels/ or its document is replaced, requests are refused with runtime_stopped and the plugins are stopped.

  9. 09

    A way back that plugins can't hide

    Plugin CSS can restyle Fermo's own page, including the settings area. So recovery lives in the Venora toolbar popup, an extension page that no plugin CSS can reach. From it you can turn on safe mode, which stops every imported plugin, or enable, disable and uninstall plugins. Open Fermo tabs apply the change at once.

03 Permissions

Permissions and consent

A permission is consent. On its own it grants nothing. It matters only because capabilities and events require it. Permissions come from plugin.json, are fixed for each installed version, and are checked on every call. An unknown permission blocks installation.

PermissionLets the plugin causeNever gives the plugin
storageReading its own settings and storing its own dataOther plugins' settings or data; Venora's or Fermo's storage
uiNotices, its declared CSS, mention avatars, the mentions.added eventDirect DOM access; page content beyond what an event reports; any change to Venora's UI
network sensitiveVenora making web requests on its behalf, inside capabilities that require itA way to send its own requests, or to see responses beyond what the capability returns
account sensitiveVenora acting with your signed-in Fermo account on its behalfThe session token, the account record, credentials, or profile data

Sensitive-permission confirmation

A permission is sensitive if it lets a plugin cause Venora to act on your behalf, send data off the device, or use your identity. Before installing, and before every update that declares one, Venora shows a warning listing them in its own top-layer dialog, and the plugin is stored only if you confirm. Anything that can't ask you fails closed.

In API v1, network and account are consent markers. No capability performs arbitrary requests or returns account data. They are needed only for the avatar lookup.

Validated before anything is stored

An imported ZIP is checked in full: structure and paths, the manifest, version, permissions, compatibility, changelog, settings, and file sizes. If anything fails, nothing is saved. Fields that only Venora may set, such as required and enabled, are stripped from imported manifests, so a third-party plugin cannot make itself impossible to disable.

04 Storage isolation

One plugin, its own data

Settings and storage are addressed by the plugin ID Venora bound to the running plugin. No method takes a plugin ID, and there is no way to list keys or other plugins. A plugin can't read, write or detect another plugin's settings or storage, or even tell whether that plugin exists.

Each plugin's data is one entry in extension storage, separate from Venora's own records and from Fermo's storage, with quotas checked before every write. Uninstalling an imported plugin deletes its data.

05 Runtime limits

Resource limits, watchdog and failures

ResourceLimitWhen exceeded
Requests waiting for an answer64 per pluginrate_limited; nothing is done
Requests, all capabilities120 at once, refilled at 60 per secondrate_limited
Costly capabilitiesTheir own limit, e.g. ui.toast: 5 at once, then 1 every 2 srate_limited
Events to a plugin100 at once, refilled at 50 per secondEvent dropped
One message50,000 values; 1,000,000 characters of strings and keysPlugin stopped
Unanswered capability30 secondstimeout; slot freed
Unresponsive workerPinged every 2 s; no answer within 5 sPlugin stopped; frame and worker removed
Startup / cleanup10 seconds / 2 secondsFailed / frame removed anyway
Sustained rejectionsMore than 200 rate_limited in 10 sPlugin stopped
Repeated problems3 stops for misbehaviour on one pageNot restarted until reload
Storage64 KiB per value; 1 MiB and 1,000 keys per pluginquota_exceeded
InstallZIP 16 MiB; manifest and CSS 131,072 characters; entry script 2,000,000Not installed

One plugin's failure doesn't stop another: accounts are per plugin, and a failed plugin's frame and page additions are removed while the others keep running.

06 Recovery

Safe mode and the toolbar popup

Safe mode

  • Stops every imported plugin and removes its CSS, without a reload
  • Available on the Plugins page and in the toolbar popup
  • Plugins bundled with Venora keep running

Toolbar popup

  • An extension page outside Fermo, so no plugin CSS applies
  • Enable or disable any optional plugin
  • Uninstall imported plugins, with a second click to confirm

07 Privacy

What Venora does with your data

Venora has no server and no account system. Its code contains no analytics or telemetry, and it syncs nothing. Here is what each party can see and keep.

What Venora itself accesses

The extension asks the browser for storage and for access to https://fermo.sbar.fyi/* only. In Fermo's page it reads the page to find mentions, and Fermo's local account data to guard your token and to look up avatars.

What a plugin can access

Only what capabilities return: its own settings and storage, and, with ui, the opaque IDs of mentions on the page with the mentioned users' public user IDs. Avatar requests return only whether an avatar was shown.

What is stored locally

In this browser profile's extension storage: imported plugin files and manifests, which plugins are enabled, safe mode, plugin settings, and each plugin's own storage. It is not synced between devices, and it is shared by every Fermo account used in that profile.

What leaves your browser

Nothing goes to Venora. When a plugin with the ui, account and network permissions shows a mention avatar, as the bundled Mention avatars plugin does, Venora may request that user's profile from the Spacebar server your Fermo account uses, and the avatar image loads from that server.

This website has no analytics, sets no cookies, and serves its fonts itself.

08 Current limitations

Where the protections stop

These are known, documented gaps. Venora would rather list them than leave them for you to discover.

  • CPU and memory aren't capped. Browsers give extensions no per-worker quota. The watchdog stops plugins that stop responding, but a plugin that stays responsive can still use CPU and memory in its own worker. Whether the frame runs in a separate process depends on the browser's site isolation.
  • Plugin CSS can change Fermo's page. With ui, CSS can restyle or hide parts of Fermo, including the settings area. It can't load resources, write text, or reach Venora's UI or the popup, and safe mode removes it at once.
  • Chromium only, and not every browser is tested. The automated browser test runs in Chromium (Microsoft Edge). Firefox has not been verified, and the sandbox must not be used there until its no-network behaviour is confirmed.
  • The automated test uses a stand-in page. It serves a page with Fermo's production security policy on Fermo's real origin. Fermo's own DOM, routing and account data are covered only by the manual test.
  • Storage is per browser profile. Plugin data is shared by every Fermo account in the profile. Writes made at the same moment from two tabs can lose one tab's change.
  • Fermo's page can stop a plugin from starting. It can see that plugin frames exist and post messages to them. It can't read the nonce or the port, but a forged connect message sent first would prevent that plugin from starting.
  • No publisher verification. There is no plugin registry or signing. When you install a ZIP, you are trusting whoever gave it to you to the extent of the permissions it declares.
  • No independent audit. The protections are specified, tested, and checked by turning each one off to confirm a test fails. No external security audit has been published.

09 Verify it yourself

Read the requirements, run the tests

The security requirements are normative: a change that breaks one is treated as a security bug. Each lists the tests that cover it. The unit tests need only Node.js 18 or later, and the browser test needs Chrome or Edge 126 or later.

Terminal · in the Venora repository
$ node --test tests/*.test.js
$ node tests/browser/chromium-e2e.mjs