Three words, three layers
A merchant thinks in Features and Integrations. A Module is how those arrive.
What a Module can add
Enabling one Module can give your Store:- public Store endpoints under
/api/... - authenticated Store Admin endpoints under
/api/admin/... - Storefront React components you can drop into MDX
- Store Admin pages, placed in the sidebar automatically
- database schema and controller behavior
- declared requirements on, and exports to, other Modules
@86d-store npm scope, with source in modules/ in the public repository.
Turn one on
Your active Template’sconfig.json holds the list:
templates/brisa/config.json
1
Read the page first
Open the Module’s page in the catalog. Check what it depends on and what it does when something fails.
2
Add the package name
Put the exact npm package name in the active Template’s
modules array.3
Regenerate
4
Prove it works
Run the affected tests, open the Storefront and Store Admin pages it added, and check its failure states, not only its happy path.
advanced.version: 1 with allowExperimentalModules: true. Writing "modules": "*", or leaving modules out entirely, discovers entries but admits no Experimental code even with the flag set.
Use a Module’s components
Enabled Storefront components are registered for MDX, so they need no import:Declared binding and storage
Every edge between Modules (capabilities, hooks, readers, template data, and durable events) is declared when the Store is provisioned and compiled into one deterministic execution graph. Nothing discovers another Module at request time. An unsatisfied, incompatible, ambiguous, or cyclic edge fails the build instead of degrading silently. Authoring contract: every Module declares one explicitstorage.kind: none, config, or relational. Relational storage contains native Zod tables with the col registry and may add config, extends, anchors, and publishes. A storage-free Module writes storage: { kind: "none" }; storage is never inferred from absence. ModuleDataService reads and writes compiled Postgres tables under mod_<moduleId> and declared Config keys only through the compiled data service.
Synchronous contract versions use stable SemVer with exact or caret (^) ranges; durable event schema versions are exact positive integers. Optional edges are allowed only when the owner Module is not installed; an installed owner with a missing or incompatible contract fails the build.
Storage kinds:
Isolation
Database isolation is the enforceable boundary: each Module runs under its own Postgres role with access to its schema and column-projected views published to it. An unpublished column is unreachable, not filtered. Request transactions enter the Module role withSET LOCAL ROLE; the login role holds no Module privileges, so a missed role switch fails closed.
In-process JavaScript isolation is not claimed. Modules share one event loop. Cross-Module decisions use typed capabilities; cross-Module reads use published views, not direct table access.
Today the runtime still carries legacy requires/exports field metadata and an in-memory event path beside the compiled durable outbox. Cross-Module decisions use typed capabilities; cross-Module reads use published views and compiled readers. See cross-Module communication.