How it works
Craft has no native concept of a plugin bundle, and it licenses plugins per handle. Showtime is a single plugin — one handle, one license — that mounts the three bundled plugins as internal Yii sub-modules rather than requiring them as separate installs.
You don’t need any of this to use Showtime. It is here because the honest answer to “how is one license legal for three plugins?” is worth writing down, and because knowing the shape of it makes the troubleshooting page make sense.
Mounted, not installed
Each mounted module is the bundled plugin’s real Plugin class, instantiated as a module of the host. It keeps its own namespace, service container, templates, controllers, database tables, and migration track. What it does not have is a row in Craft’s plugins table — it is never registered with Craft’s Plugins service.
That is the whole trick. Craft only asks installed plugins for a license. A mounted module isn’t one, so it is never asked. Only Showtime’s license is enforced. All three are my own plugins, so there is no third-party licensing being worked around.
What mounting has to reproduce
Craft’s Plugins service does five things for an installed plugin that a plain module doesn’t get for free. The mount reproduces all five:
- Merge the plugin’s
config()— notably itscomponentsservice map, without which none of its services resolve. - Supply stored settings. Craft merges settings into the construction config, which only happens for installed plugins — so a mounted module would otherwise silently run on defaults. Showtime owns the settings and hands each module its slice at mount time (see Configuration).
- Set
isInstalledandedition. Editions matter here: Craft never license-checks a mounted module, so Owl’s Pro edition is asserted at mount — without that, Owl would quietly run as Lite and Commerce ticketing would never boot. - Set the
migratorcomponent. Craft sets this on a plugin from the outside; without it, nothing can install or migrate. - Register the module so
actions/<handle>/…controller routing resolves.
Plus one flag of its own, mountedUnderShowtime, which tells each plugin to boot its features but skip the standalone control-panel chrome the host now owns — its own nav item, its own permission heading. Everything else about it runs unchanged, and the flag defaults to false, so standalone behaviour is untouched.
The migration track is the same one
Each mounted module migrates under plugin:<handle> — deliberately the same track name the standalone plugin uses, and against the same table names. That single decision is what makes adoption work in both directions: a site that adopts Showtime keeps its migration history and its data, and a mounted module’s data is exactly what a standalone install would expect to find.
Showtime’s own install migration doesn’t create anyone else’s tables. It delegates to each module’s install(), and each module’s schema lands under its own track.
Adoption
Installing Showtime on a site already running one of the bundled plugins absorbs it in place. Three things change — the settings are re-homed under the host, the plugins row is deleted, and the plugin’s project-config node is removed — and nothing else. The uninstall migration is deliberately not run and the migration track is left intact, so not a row of data is touched. Full detail on the Upgrading page.
The glue is all listeners
Every cross-module behaviour — member perks, the unified nav, the combined permission heading, the dashboard — lives in the host, implemented as listeners on the modules’ own events and reads through their own public services.
None of the three learns that the others exist. That constraint is what lets the glue be as opinionated as it likes while all three keep shipping standalone, and it is why the perks table (showtime_perks) is the one table the host owns: a link between a membership plan and a bookable service is a relationship neither plugin could own without depending on the other.
Source of truth
The bundled code under src/modules/* is a one-way vendored copy of each plugin’s own repository, synced by a script. The canonical source for each is still its own repo — fixes go there and get re-synced, never edited in place. In practical terms: the code you get in the bundle is byte-for-byte the code you would get buying the plugin on its own.
Guardrails
- Stub and Headcount refuse to install standalone on a site where Showtime is already installed — a second copy would register a duplicate element type and share the same tables.
- The host asserts Owl’s Pro edition at mount, so the bundle can never silently degrade to Lite.
- A console harness asserts the entire mount contract against a real Craft and database on every release — licensing, mount, migrator paths, schema, settings propagation, services, element round-trips, templates, published assets, and route resolution — and it is run on both a clean install and an adoption over live data.
The plugin’s own repository carries the full architecture write-up if you want to go further.