managed-pages
Tenant-editable, server-rendered public pages with per-tenant branding. Stores one Markdown page per (tenantId, slug, lang) in the read_pages entity table with a published gate plus description/ogImage SEO meta. Registers an anonymous GET {basePath}/:slug route that resolves the tenant from the request Host via the app-supplied resolveApexTenant, serves only published pages (drafts → 404), renders Markdown through the hardened page-render core, and isolates per-tenant content with Vary: Host. Ships TenantAdmin/SystemAdmin admin screens (entityList + entityEdit) backed by convention CRUD handlers (managed-pages:write:page:{create,update,delete}, managed-pages:query:page:{list,detail}); the app wires nav/workspace onto managed-pages:screen:page-list. Also exposes managed-pages:query:by-tenant-published (anonymous, SQL-filtered on published) for sitemap/discovery consumers such as the seo feature. Branding (via config, scope tenant): branding-{title,description,site-url,accent-color,logo-url,layout-preset} keys with write-time validation (hex color, https URLs), a configEdit self-service screen (managed-pages:screen:branding-settings), and a managed-pages:query:branding read that the render path applies as scoped :root CSS vars + a logo/title header. Also exposes managed-pages:write:set (idempotent slug-keyed upsert, SystemAdmin cross-tenant via tenantIdOverride) as a provisioning API. Requires config + anonymousAccess wired at app bootstrap.
Quick example
Section titled “Quick example”From recipes-managed-pages — the smallest working mount:
import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";import { createManagedPagesCssFeature, createManagedPagesFeature,} from "@cosmicdrift/kumiko-bundled-features/managed-pages";import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
// managed-pages declares `r.requires("config")` for its branding keys — the// config feature must be in the stack (runProdApp auto-mounts it; setupTestStack// does not, so the recipe lists it explicitly).export const configFeature = createConfigFeature();
// Single-tenant apex: every request maps to the one system tenant. A multi-// tenant app resolves the tenant from the request Host (subdomain / custom// domain) and returns the matching tenantId instead — see README.export const managedPagesFeature = createManagedPagesFeature({ resolveApexTenant: () => SYSTEM_TENANT_ID, allowCustomCss: true,});
// Companion per-tenant toggle for the CSS-inject capability. Compose it and wire// feature-toggles to gate custom CSS per tier; without a toggle runtime,// `ctx.hasFeature` fails open and CSS stays on (the render-time sanitizer is the// safety boundary regardless).export const managedPagesCssFeature = createManagedPagesCssFeature();📄 On GitHub: samples/recipes/managed-pages/src/feature.ts
Live preview
Section titled “Live preview”
How it fits
Section titled “How it fits”What this feature needs to run (Requires, top) and the write commands it provides (Provides, bottom).
flowchart TB
n_managed_pages["managed-pages"]
subgraph how_reqs["Requires"]
n_config["config"]
end
subgraph how_provides["Provides"]
n_cmd_managed_pages_write_page_create(["create"])
n_cmd_managed_pages_write_page_delete(["delete"])
n_cmd_managed_pages_write_page_update(["update"])
n_cmd_managed_pages_write_set(["set"])
end
n_config --> n_managed_pages
n_managed_pages --> n_cmd_managed_pages_write_page_create
n_managed_pages --> n_cmd_managed_pages_write_page_delete
n_managed_pages --> n_cmd_managed_pages_write_page_update
n_managed_pages --> n_cmd_managed_pages_write_set
Provides — write commands this feature registers (dispatch them through the command bus):
managed-pages:write:page:createmanaged-pages:write:page:deletemanaged-pages:write:page:updatemanaged-pages:write:set
Getting started
Section titled “Getting started”Start with recipes-managed-pages for a step-by-step walkthrough with runnable code and integration tests.
Dependencies
Section titled “Dependencies”- Requires:
config - Activation: always on (not toggleable)
Configuration
Section titled “Configuration”Per-tenant config keys, set via the tenant-admin UI or a seed. 🔒 = encrypted at rest.
| Key | Type | Default | Scope | Who can write | Who can read |
|---|---|---|---|---|---|
branding-accent-color | text | "" | tenant | system, TenantAdmin, Admin, SystemAdmin | all |
branding-custom-css | text | "" | tenant | TenantAdmin, Admin, SystemAdmin | all |
branding-description | text | "" | tenant | system, TenantAdmin, Admin, SystemAdmin | all |
branding-layout-preset | select (minimal | centered | wide) | centered | tenant | system, TenantAdmin, Admin, SystemAdmin | all |
branding-logo-url | text | "" | tenant | system, TenantAdmin, Admin, SystemAdmin | all |
branding-site-url | text | "" | tenant | system, TenantAdmin, Admin, SystemAdmin | all |
branding-title | text | "" | tenant | system, TenantAdmin, Admin, SystemAdmin | all |