Security model

How Athanor runs plugins safely: the QuickJS sandbox in a worker, time and memory limits, the sandboxed window and its CSP, network rules and signing.

In one paragraph

A plugin's code runs in its own small JavaScript engine, inside its own background worker, where the only thing it can reach is the athanor object. Every call it makes is a message that Athanor answers only if the plugin holds the permission for it. Its window is a separate locked-down frame that can talk only to the plugin. It never edits your document directly, it never sees your account, and if it misbehaves it is stopped while Athanor carries on.

The logic: an engine inside a worker

  • Its own engine. main.js runs in QuickJS, a separate JavaScript engine compiled to WebAssembly. It is not the browser's JavaScript with a few things hidden: the plugin simply has no handle on anything of Athanor's or the browser's. It sees standard JavaScript, the athanor object, console, and setTimeout/clearTimeout provided by the host. There is no fetch, no DOM, no importScripts and no Worker.
  • Its own worker. Each plugin's engine runs in its own Web Worker, separate from the app and from every other plugin. Athanor can end the worker at any moment, so even a fault in the engine itself cannot freeze the app.
  • Only data crosses. Everything between the plugin and Athanor is plain JSON. No object of Athanor's goes in and none of the plugin's comes out.

Time and memory limits

Limit Community plugins Official plugins
One slice of synchronous code (a handler, plus the promise work it starts) 5 seconds 15 seconds
Memory 64 MB 256 MB
Changes applied per second 5 20
Check run 300 ms 1 second
Toasts per minute 12 12

A slice that runs past its time is interrupted, and if the engine does not answer two seconds after that, Athanor ends the worker. Either way the person sees "Plugin stopped responding. Your work is safe." Running out of memory reads "Plugin used too much memory. Your work is safe." The plugin starts again the next time it is used. Three failures in ten minutes turn it off for the session, with Turn back on.

The document is never touched directly

A plugin reads a snapshot and writes by sending a change. Athanor checks the permission, checks the change's shape and limits, runs the chemistry safety screen on every structure the change adds or alters, and applies it as one undo step. If any op is refused, the page is rolled back to exactly how it was. A crash in the middle of a change leaves nothing behind. See Changes, undo and previews.

The window: a sandboxed frame

A plugin's ui.html loads in an <iframe sandbox="allow-scripts">, without allow-same-origin. That gives it an opaque origin: no cookies, no storage, and no access to the app's page. It cannot submit forms, open popups, navigate the app or start downloads, and it sends no referrer.

The page carries this Content Security Policy:

default-src 'none'; script-src 'unsafe-inline' blob:; style-src 'unsafe-inline';
img-src data: blob:; font-src data:; connect-src <declared sites, or 'none'>;
form-action 'none'; base-uri 'none'

So nothing loads from outside: no remote scripts, stylesheets, images or fonts. connect-src is 'none' unless the plugin holds Access the internet, and then it lists only the declared sites, over HTTPS.

The window talks only to its own plugin's logic. Athanor relays each message and checks it came from that plugin's frame.

Athanor draws the window's title bar, name, Official badge and close button itself, so a plugin cannot draw something that passes for an Athanor dialog. Proposed changes always appear in a box that names the plugin.

Network rules

A plugin reaches the internet only with the network permission, only from its window, and only to the sites it declared, which you see before installing. Published plugins must name each site exactly; wildcards are refused. Requests carry no Athanor cookies or sign-in. See Network access.

Your account stays yours

A plugin never sees your password, your email address or your Athanor session. Its engine has no access to the app's storage or sign-in, and its window has an opaque origin with no cookies. Installing, rating and publishing are done by Athanor itself, on your behalf, never by the plugin.

Permissions

Plugins get only the permissions they declare, and you can switch any off after installing; the plugin restarts at once with the smaller set. An update that asks for more waits for your OK. See Using plugins.

Where published code comes from

Community plugins are downloaded only from Athanor's own storage. Before loading one, Athanor checks the package's SHA-256 hash against the published version record and verifies Athanor's Ed25519 signature over the plugin id, version, hash, permissions and sites. A package that fails either check is not installed. The official plugin ships inside the app itself. Development plugins are unsigned and run only on the device that loaded them.

If a plugin is taken down, Athanor turns it off everywhere: the app checks for taken-down plugins when it starts and every six hours.

Reporting a security problem

Use Report plugin on the plugin's detail page and choose Malicious behavior or Privacy or data concern. The Review Guidelines describe what happens after a report.

Is this helpful?

Last updated Nov 11, 2026