Separating the Control Plane from the Output Plane
A fleet like this one doesn't just run services, it generates artifacts. Every merchant site is a real, deployed application, cloned from a shared template and baked with that merchant's own content. It's tempting to treat a generated artifact as an extension of the fleet itself, since the fleet built it, and let it reach back into internal services through whatever channel is convenient because "it's our own code anyway." Resist that. The generated site is a client, the same as any other client, and it should look like one in the code.
The control plane is the set of services that manage the fleet: api-server, api-license, api-git, api-domain, api-ai, and merchants as the app a human operates. The output plane is the code running inside each generated site, cloned from the shared template repository and layered with the package that delivers the theme and its features. The output plane's job is to render a merchant's business. The control plane's job is to run the fleet. The moment a generated site gets a special, unversioned side channel into the control plane because it "came from us," that boundary starts leaking. Fleet-management concerns, routing, ops dashboards, service-to-service auth, have no business showing up inside code that's supposed to be a small, stable, artifact-facing contract.
What keeps that boundary honest in practice is a plain configuration file, checked into every generated site, that both sides agree on:
// config/webplo.php
return [
'brand' => [
'name' => 'Example Business',
'phone' => '(800) 000-0000',
'address' => 'New York',
'language' => 'en',
'locale' => 'en_US',
'index' => false,
],
'networks' => [
'facebook' => null,
'instagram' => null,
],
'colors' => [
'primary' => '#D97757',
'variant' => '#ffffff',
],
];
This is the entire content contract between the two planes. The control plane's job is to know what a merchant's business looks like; it writes that knowledge into a file like this one. The output plane's job is to render a site from whatever this file says; it never has to know how that knowledge was produced, and the control plane never has to know how it's rendered. Neither side reaches past the file to grab something the other one didn't publish.
Why bother drawing this line at all, instead of letting a generated site call whatever internal endpoint gets the job done fastest? Because "fastest" today is a maintenance debt tomorrow. A site is cloned once and then lives for years, deployed independently, upgraded on its own schedule through the theme package's own installer, not through you personally patching files inside it. If that site's code has learned to depend on some internal shape of api-server directly, every future change to api-server now has to consider every already-deployed site as a caller it can't see and can't coordinate with. Keep the artifact-facing surface small, stable, and explicit, and api-server stays free to change everything behind it.