# 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.

Source: https://shopfa.st/docs/plugins

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](https://shopfa.st/docs/plugins/payment) | `create_payment`, `verify_callback` | A buyer picks the plugin's method at checkout; the provider calls back |
| [Delivery](https://shopfa.st/docs/plugins/delivery) | `deliver` | An order with one of the plugin's products is paid |
| [Checkout rule](https://shopfa.st/docs/plugins/checkout) | `validate_cart` | Before every checkout starts |
| [Webhook transformer](https://shopfa.st/docs/plugins/transformers) | `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.

```lua
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`](https://shopfa.st/docs/plugins/host-api#http) 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](https://shopfa.st/docs/plugins/host-api#kv). 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](https://shopfa.st/docs/plugins/catalog). The workbench has live linting in the sandbox, completions for the host API, a
[test runner with fixtures](https://shopfa.st/docs/plugins/testing), live runs with their logs and metrics, settings and versions.
