Docs
Dashboard
View as Markdown
On this page
  1. Four kinds
  2. The sandbox
  3. Permissions you approve
  4. Versions and roll back
  5. Where to write them

How plugins work

Lua 5.4 plugins that run in ShopFast's sandbox, with a typed host API, declared permissions you approve, versions and roll back.

Plugins let a store add what ShopFast does not ship: a payment provider, a key generator, a fraud rule, a webhook format. They are a few dozen lines of Lua, run next to the checkout in ShopFast’s sandbox, and never need a server.

Four kinds

Kind Hooks Runs when
Payment method create_payment, verify_callback A buyer picks the plugin’s method at checkout; the provider calls back
Delivery deliver An order with one of the plugin’s products is paid
Checkout rule validate_cart Before every checkout starts
Webhook transformer transform Before an event is signed and sent to an endpoint it is attached to

A plugin is two files: plugin.lua, which returns a table of hook functions, and manifest.json, which declares what it is and what it may do.

local plugin = {}

function plugin.validate_cart(ctx, input)
  if input.cart.emailDomain == "mailinator.com" then
    return { action = "block", message = "Please use a permanent email address.", reason = "disposable inbox", points = 80 }
  end
  return { action = "allow" }
end

return plugin

The sandbox

Each run gets a fresh Lua 5.4 state (WebAssembly, in a worker thread) that is thrown away afterwards. Nothing leaks between runs, plugins or stores.

  • Only safe libraries. string, table, math, utf8, coroutine and the safe parts of the base library. No io, os, debug, package, require, load, dofile or string.dump; bytecode never runs.
  • No JavaScript objects. Plugin code only sees Lua values; host functions take and return strings and numbers.
  • Budgets per run: VM instructions, CPU time (time spent waiting on the network does not count), heap size and a wall-clock deadline after which the worker is terminated. A spent budget cannot be caught: every pcall, xpcall and coroutine.resume re-raises it.
  • No network except http.fetch to hosts the manifest lists, through an SSRF-safe proxy: https only, no private or metadata addresses, no redirects, 5 s per request.
Hook Instructions CPU Memory HTTP requests Wall clock
create_payment, verify_callback 5M 500 ms 16 MB 4 12 s
deliver 10M 1 s 32 MB 4 15 s
validate_cart 2M 200 ms 8 MB 1 3 s
transform 2M 250 ms 8 MB 0 2 s

Permissions you approve

The manifest’s permissions say what the plugin may do beyond computing: the hosts http.fetch may call and whether it gets a key-value store. Secret settings, the kind’s own access (for example “start payments at checkout”) and these permissions are shown to the store owner or an admin in plain words. A version runs only after its exact set of permissions is approved; a new version that asks for more needs a new approval, and a version whose permissions changed since the approval never runs.

Versions and roll back

Saving creates an immutable version with its source, manifest and SHA-256. Nothing changes until you activate one. Activating an older version is the roll back, and it is instant. Every live run records the version it ran.

Where to write them

Open Settings → Developers → Plugins: start from a template of each kind, or install a reviewed package from the catalog. The workbench has live linting in the sandbox, completions for the host API, a test runner with fixtures, live runs with their logs and metrics, settings and versions.

Was this page helpful?