Modules and bundles
What the core platform provides, what each Meridian module adds and requires, which modules the engine-cms and engine-dmo bundles compile, and what happens when a project uses a module its binary lacks.
Meridian is a core content platform plus modules. A bundle is the core and a fixed list of modules compiled into one binary: engine-cms and engine-dmo. You choose a bundle by choosing the binary; you cannot add or remove a module from a binary, and nothing in a project’s configuration switches one on.
Your own code is not a module. It is an extension, a Go plugin the engine loads at start; see Extending.
The core
Every bundle has the core platform:
- the five core document types
page,article,event,guideandcollection, and your custom types; - every field type, validation, relations, localisation and entry variants;
- documents, revisions, the publishing workflow, scheduling and the trash;
- multiple sites and locales, routes, redirects, feeds, calendars and sitemaps;
- the media library and image pipeline;
- search, navigation, tags and smart lists;
- declared endpoints, sources, importers, outbound webhooks and plugins;
- analytics, dashboards and content health;
- the admin, staff users, roles and grants;
- the content API and the content MCP;
- the template language and its tooling.
The modules
| Module | Adds | Requires | engine-cms | engine-dmo |
|---|---|---|---|---|
AI (ai) | AI assistance in the admin with your own provider: suggestions, alt text, a review queue, a budget and the activity log; the ai: section of engine.yaml; engine-dmo ai | Yes | Yes | |
Forms (forms) | The store step of declared endpoints and the submissions it keeps, their inbox in the admin, the drawn forms (enquiryForm(), subscribeForm()) and their abuse controls; the forms: section | Yes | Yes | |
Newsletter (newsletter) | Subscribers and their consent, double opt-in, campaigns built from live content, the deliverability wizard, the preference centre; engine-dmo newsletter | Yes | Yes | |
Translation (translation) | Translation jobs and the Translations workspace, translation memory, the glossary, adding a locale to existing content; engine-dmo translate; the translation: section | Yes | Yes | |
Directory (directory) | The listing and area types, landing pages, and the embeddable widgets | Yes | ||
CRM (crm) | Organisations and people, their custom fields (config/crm/), partner roles (config/portal/roles.yaml), broadcasts to member contacts, the import and the merge; engine-dmo crm, engine-dmo broadcast | Forms | Yes | |
Memberships (memberships) | Membership tiers (config/memberships.yaml), which tiers each site sells, memberships with renewals and reminders; the membership: section | CRM | Yes | |
Member portal (portal) | The member realm: invitations and sign-in, the member portal and the member API (config/portal/, each site’s portal.yaml); engine-dmo portal | CRM, Memberships | Yes | |
Claims (claims) | A person claiming an organisation, the checks and the decision, the staff queue and the portal’s claim flow | CRM, Memberships, Member portal | Yes | |
Promotions (promotions) | Promotion slots (each site’s promotions.yaml), members’ requests for them, the staff queue, and promoted() in templates | CRM, Memberships, Member portal | Yes | |
Member report (memberreport) | The member report in the portal, the member API, as a PDF and as a digest; config/analytics/reports.yaml | CRM, Memberships, Member portal | Yes | |
Affiliates (affiliates) | Affiliate programmes, tracked links marked sponsored, conversion imports and postbacks; engine-dmo affiliates | CRM | Yes | |
Paid listings (paidlistings) | Prices for tiers and promotions, orders, payment through a provider’s hosted checkout or a staff invoice; config/paidlistings.yaml | CRM, Memberships, Member portal, Promotions | Yes |
The modules that require others form one chain. Each arrow points at a module it requires, and a module also requires what that one requires (Paid listings needs Promotions, so it needs the Member portal, Memberships, CRM and Forms too). AI, Newsletter, Translation and Directory require nothing.
flowchart RL crm["CRM"] --> forms["Forms"] memberships["Memberships"] --> crm affiliates["Affiliates"] --> crm portal["Member portal"] --> memberships claims["Claims"] --> portal promotions["Promotions"] --> portal memberreport["Member report"] --> portal paidlistings["Paid listings"] --> promotions
The DMO section documents the modules only engine-dmo has. The AI, Forms, Newsletter and Translation modules are documented under Features because both bundles have them.
What a module contributes
A module can add any of these, and they exist only in a binary that compiles it:
| Contribution | Where you meet it |
|---|---|
| Content types | listing and area from Directory. A project can extend them in config/schema/ |
| Configuration files | Such as config/memberships.yaml or a site’s promotions.yaml |
A section of engine.yaml | Such as ai:, forms:, portal: or membership: |
| Database tables | Each module has its own migration stream, named in engine-dmo migrate status and migrate to <module>/<version> |
| Permissions | Staff permission verbs, granted to built-in roles (below) |
| Admin screens, CLI commands, template functions, content MCP tools and seed sections | Each listed by engine-dmo help --json, engine-dmo template api and engine-dmo describe |
Using a module the binary does not have
A project that uses a module’s file, section or type on a binary that does not compile it is refused at the place it is used, naming the module, by check and at boot. On engine-cms:
config/memberships.yaml:1:1: memberships.yaml belongs to the memberships module, which this bundle (cms) does not compile
1 | tiers:
| ^
= use a bundle that compiles memberships, such as the DMO platform's bin/engine-dmo, or remove it
= error code MRD-1006 config/engine.yaml:22:1: portal: belongs to the portal module, which this bundle does not compile
22 | portal:
| ^^^^^^
= use a bundle that compiles portal, or remove the section
= error code MRD-1006 config/schema/listing.yaml:1:10: "listing" is not a core type; core types are page, article, event, guide, collection
1 | extends: listing
| ^^^^^^^
= to declare a custom type use key: instead of extends:
= error code MRD-1006 The reverse always works: every module of engine-cms is in engine-dmo, so an engine-cms project runs on engine-dmo unchanged. A database that engine-dmo has migrated can be opened by engine-cms; the DMO modules’ migrations are reported in the log as belonging to modules this bundle does not compile, and their tables are left alone.
Modules you do not use
Every compiled module is present whether or not your project uses it, so choosing engine-dmo for a site that needs none of the DMO modules costs one thing worth knowing: the member portal is on. engine-dmo describe project shows it on each site, as portal /members, both, and the portal answers under /members, where a visitor is redirected to its sign-in.
Turn it off for the whole install in config/portal/portal.yaml:
# config/portal/portal.yaml
portal:
mode: "off" describe project then shows portal /members, off, and nothing is served under the portal path. A site’s own portal.yaml can do the same for one site (Partners and the portal). Quote "off": YAML reads it unquoted as well, but quoted it cannot be mistaken for a boolean.
The other DMO modules do nothing until you configure them: the listing and area types exist but have no pages until a route names them, and there are no tiers, slots or programmes until their files declare some. If your site uses none of them, engine-cms is the smaller binary, but no published image contains it (Install).
Permissions a module adds
Each module’s verbs join core’s in config/access/roles.yaml, and some are given to built-in roles. The owner role holds every verb of every compiled module.
| Module | Verbs | Built-in roles that hold them |
|---|---|---|
| AI | ai.use, ai.bulk, ai.generate_image, ai.configure | owner only; grant them to your roles |
| Forms | submissions.view | administrator, partnership-manager |
| Newsletter | newsletter.view, newsletter.manage, newsletter.draft, newsletter.send, newsletter.export | administrator all; editor view and draft; analyst view |
| CRM | broadcast.draft, broadcast.send, crm.import | administrator, partnership-manager |
| Promotions | promotions.decide | administrator, partnership-manager |
| Affiliates | affiliates.view, affiliates.manage | administrator and partnership-manager both; analyst view |
| Paid listings | paidlistings.manage | administrator, partnership-manager |
A role naming a verb no compiled module declares is an error at its line, with the nearest known verb suggested. Users, roles and permissions explains roles and grants.
Finding out what a binary has
engine-dmo version # the bundle and its modules, one line
engine-dmo version --json # the same, with each module's id and name
engine-dmo help --json # every command, with its arguments and flags
engine-dmo describe project # the types, sites, roles and other elements the project has under this binary meridian v0.8.0 (pure-go) go1.27.3
bundle dmo: modules ai directory forms crm affiliates memberships newsletter portal claims memberreport promotions paidlistings translation Documents, revisions and publishing
How Meridian stores content as documents with a shared revision chain and one chain per locale, the per-locale workflow states, what publishing checks and does, scheduling, the trash, and revision retention.
Stable ids
The id written beside every named element in Meridian configuration, who writes it, why a rename keeps all stored data, how renames and aliases work, and how to fix a missing, copied or wrong id.