Mount the foundation
Kumiko ships identity, auth, and multi-tenancy as bundled features. You do not wire login, user tables, or tenant schemas by hand. List your own features in one function and flip one flag for the schema CLI.
buildAppFeatures
Section titled “buildAppFeatures”src/run-config.ts is the single source of truth for what your app mounts.
The sample no longer exports a static APP_FEATURES array. It builds the list
from tenant routing (base domain) so dev, prod, and schema generation stay aligned:
export function resolveBaseDomainFromEnv(): string { return process.env["BASE_DOMAIN"] ?? "show-pony.localhost";}
export function buildAppFeatures(routing: AppFeaturesRouting): FeatureDefinition[] { return [ localeDe(), authFoundationFeature, createShowPonyTenantRoutingFeature({ baseDomain: routing.baseDomain }), createTierEngineFeature({ defaultTier: DEFAULT_TIER, tierMap: SHOWPONY_TIER_MAP }), billingFoundationFeature, // mail, admin shell, app shell, showPonyFeature, … ];}
export const HAS_AUTH = true;src/run-config.ts.
buildAppFeatures pulls in tenant routing, billing, tier limits, and the
operational plugins (mail, jobs, audit, …) alongside showPonyFeature.
HAS_AUTH = true is consumed by bin/kumiko.ts when generating schema:
composeFeatures([...buildAppFeatures(...)], { includeBundled: HAS_AUTH }).
At runtime, runDevApp / runProdApp call composeFeatures automatically
when you pass an auth: block. That adds user, tenant, and email/password
login without listing them in run-config.ts.


What you get for free
Section titled “What you get for free”After bun dev:
- Email/password login at
/loginon the apex (show-pony.localhost:4180): chapter 12 adds the public landing on/ - A seeded demo host:
[email protected]/changeme - A seeded sysadmin:
[email protected]/changeme(platform workspace): chapter 8) - Tenant-scoped writes and queries, with isolation from the framework rather than custom middleware
- Admin shell (sidebar, topbar) ready for schema-driven screens in chapter 8
Boot wiring
Section titled “Boot wiring”bin/server.ts spreads buildAppFeatures({ baseDomain: BASE_DOMAIN }) into
runDevApp, seeds the demo admin, and sets up host routing (apex vs subdomain;
detailed in chapter 4).
Demo events + RSVPs are seeded only in production (seedsDir in
bin/main.ts, not on bun dev). Locally
you create events in the dashboard or follow chapter 11.
The complete file
Section titled “The complete file”// Single source of truth for show-pony feature composition.// Both bin/server.ts (dev) and the kumiko-schema CLI build on this,// so the runtime registry and generated schema never drift apart.//// HAS_AUTH=true → composeFeatures automatically pulls in the bundled auth chain// (config/user/tenant/auth-email-password/secrets).
import { createAdminShellFeature } from "@cosmicdrift/kumiko-bundled-features/admin-shell";import { createAuditFeature } from "@cosmicdrift/kumiko-bundled-features/audit";import { authFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";import { billingFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/billing-foundation";import { createComplianceProfilesFeature } from "@cosmicdrift/kumiko-bundled-features/compliance-profiles";import { createCryptoShreddingFeature } from "@cosmicdrift/kumiko-bundled-features/crypto-shredding";import { createJobsFeature } from "@cosmicdrift/kumiko-bundled-features/jobs";import { mailFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/mail-foundation";import { mailTransportInMemoryFeature } from "@cosmicdrift/kumiko-bundled-features/mail-transport-inmemory";import { createManagedPagesFeature } from "@cosmicdrift/kumiko-bundled-features/managed-pages";import { createRateLimitingFeature } from "@cosmicdrift/kumiko-bundled-features/rate-limiting";import { createSecretsFeature } from "@cosmicdrift/kumiko-bundled-features/secrets";import { createSessionsFeature } from "@cosmicdrift/kumiko-bundled-features/sessions";import { createTemplateResolverFeature } from "@cosmicdrift/kumiko-bundled-features/template-resolver";import { createTenantLifecycleFeature } from "@cosmicdrift/kumiko-bundled-features/tenant-lifecycle";import { createTierEngineFeature } from "@cosmicdrift/kumiko-bundled-features/tier-engine";import { composePagesStack } from "@cosmicdrift/kumiko-dev-server/compose-stacks";import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";import { localeDe } from "@cosmicdrift/kumiko-locale-de";import { appShellFeature } from "./features/app-shell/feature";import { showPonyFeature } from "./features/show-pony/feature";import { DEFAULT_TIER, SHOWPONY_TIER_MAP } from "./features/show-pony/tier-map";import { renderLegalLayout } from "./legal-layout";import { createShowPonyTenantRoutingFeature, resolveSubdomainPageTenant } from "./tenant-routing";
/** Overview screens + nav only — app-shell owns workspaces host/platform. */const adminShellFeature = createAdminShellFeature({ registerWorkspaces: false, includeTierAdmin: true,});
export type AppFeaturesRouting = { readonly baseDomain: string;};
/** $BASE_DOMAIN or show-pony.localhost — the one place that default lives. */export function resolveBaseDomainFromEnv(): string { return process.env["BASE_DOMAIN"] ?? "show-pony.localhost";}
export function buildAppFeatures(routing: AppFeaturesRouting): FeatureDefinition[] { return [ localeDe(), authFoundationFeature, createSessionsFeature(), createShowPonyTenantRoutingFeature({ baseDomain: routing.baseDomain }), createTemplateResolverFeature(), ...composePagesStack({ wrapLayout: renderLegalLayout }), createManagedPagesFeature({ resolveApexTenant: resolveSubdomainPageTenant, allowCustomCss: false, }), mailFoundationFeature, mailTransportInMemoryFeature, createRateLimitingFeature(), createAuditFeature(), createJobsFeature(), createTierEngineFeature({ defaultTier: DEFAULT_TIER, tierMap: SHOWPONY_TIER_MAP }), createComplianceProfilesFeature(), createTenantLifecycleFeature(), billingFoundationFeature, createCryptoShreddingFeature(), createSecretsFeature(), adminShellFeature, appShellFeature, showPonyFeature, ];}
export const HAS_AUTH = true;📄 On GitHub: src/run-config.ts