StoreConfig.
One config, six surfaces
The six surfaces (general, store, checkout, overlay, portal, email) are the tabs in the builder. general holds the brand tokens that cascade everywhere; each other tab overrides locally.
Draft, publish, discard
Edits are staged indraft and only go live on Publish, exactly like a CMS:
A PATCH to a surface deep-merges into the draft (so toggling one portal permission doesn’t wipe the rest); hasUnpublishedChanges is computed by comparing draft to published, driving the “Unpublished changes” badge. The merge is bounded — surface level plus one nested level, which is all the config shape needs, so there’s no unbounded recursion. Verified live: per-surface deep-merge including nested permissions, publish, and discard-reverts-to-published.
The portal permissions live here
The customer portal asks this config what it’s allowed to offer —portal.permissions.cancelSubscription, pauseSubscription, updatePlan, etc. A merchant who disables cancellation in the builder disables it in the portal. Configuration and enforcement are the same source of truth.
Powered by RBAC
Editing appearance needs theappearance:manage permission (owner, admin, and developer roles have it) — see the security model. The published config is read by the public surfaces without auth; only the editing is gated.
Next: the security model.