Kytelo is multi-tenant commerce infrastructure: one platform, many shops, each isolated by row-level security in a single Postgres database. What makes it interesting to build on is the extension model — this page is the map before theme development and plugin development get concrete.
The one rule#
Every extension contract is a pure, serializable data transform. Filter handlers take a JSON-shaped context and return a JSON-shaped value; events and jobs are JSON payloads; section loaders return JSON; merchant theme customization is JSON. Nothing that crosses a plugin or theme boundary is a live object — no class instances, no callbacks, no shared mutable state.
That constraint is deliberate, not incidental. It's what lets the same contract move from "runs in-process today" to "runs in a WASM sandbox or an edge isolate tomorrow" without a redesign — the same reason Shopify Functions (data in, data out) scaled where WordPress hooks (live PHP objects, callable anything) eventually couldn't.
Sections and page documents#
A section is the unit of composition: a schema-described building
block (a hero banner, a product grid, a cart drawer) that a theme or a
plugin contributes into one shared registry. Some sections have blocks
— sub-items a merchant can add, remove, and reorder inside the section
(a product page's title/price/buy-buttons/description column, for
example, is four blocks inside one product-detail section).
A page document is merchant content: which section instances appear on a page, their settings, their block order — stored as JSON, validated against the registry on every read, and rendered by whichever engine the active theme uses. If a stored document ever references a section that no longer exists, the platform falls back to the theme's bundled template rather than serving a broken page.
Themes and plugins share one registry#
Both themes and plugins contribute sections into the same registry — that's how a theme with no idea what plugins are installed can still render a cart drawer or search results: the plugin's section fills a slot the theme's page documents reference by section type, not by import. See theme development for building the theme side of this, and plugin development for the plugin side.
Plugins in one paragraph#
A plugin is a manifest (identity, and every capability it needs, declared
up front) plus the registrations that back those capabilities: filters
(priority-ordered transforms at a named point — pricing a cart, computing
related products), events (subscribing to platform facts like
product.created, always after the write that caused them succeeded),
jobs (queued background work the platform worker executes), and
sections (theme-renderable UI, as above). Every registration must be
both declared in the manifest's lists and covered by a capability string —
checked when the plugin is defined, and again at boot. See
plugin development for the real thing,
built from the cart plugin.
Sandboxing, today and later#
Themes and plugins are first-party and reviewed marketplace code, not arbitrary user code — but the platform is built assuming that changes. Today (tier 1) that means static enforcement: a theme's imports are restricted to its allowed contracts (checked by both ESLint and a dedicated validator — see theme development's authoring constraints), and a plugin's registrations are checked against its declared capabilities. Later tiers add AI-assisted review for marketplace submissions, and eventually runtime isolation (WASM for hot-path functions, isolates for richer plugins). None of that changes the contract shape — it only changes how strictly it's enforced.
What's next#
- Theme development — build a real theme, from a section loader to the settings a merchant edits.
- Plugin development — build a plugin, using the shipped cart plugin as the worked example.
- Local development — run the whole stack to try any of this yourself.