Security baseline
Mount composeSecurityStack() instead of hand-picking the four features
every prod app needs for basic account/security hygiene: sessions
(revocable JWTs), crypto-shredding (operator-triggered subject-key erase),
rate-limiting (ops-side bucket-status query, dispatcher wiring is
automatic), and audit (tenant-scoped audit trail). Forgetting one of them
used to be silent; now the boot validator warns at NODE_ENV=production.
What it shows
Section titled “What it shows”composeSecurityStack()(@cosmicdrift/kumiko-dev-server/compose-stacks) — returns the four features in one call, fresh instances per call. It replaces the deprecatedsecurityBaselineFeatures()preset.runProdApprefuses to boot it without a KMS decision (kmsMode) or withrateLimit: false; seecompose-stacks.warnOnMissingSecurityBaseline— wired intovalidateBoot(fw#2857). AtNODE_ENV=production, boot compares mounted feature names againstSECURITY_BASELINE_FEATURE_NAMESand logs aconsole.warnnaming whatever is missing. It never throws — the baseline is a strong recommendation, not a hard requirement — and it is silent outside production.includeSessions: false— an optional opt-out for droppingsessionsfrom the stack explicitly. Not required for combining withcomposeGdprStack({ selfService }): both mountcreateSessionsFeature()with no-arg options, and the framework’sdedupeFeatures()collapses the two identical instances at boot instead of throwing.
Feature composition
Section titled “Feature composition”config → tenant's r.requires({ features: ["config"] }) dependencyuser → cross-tenant identity (sessions' r.requires target)tenant → memberships (audit's r.requires target)auth-foundation → tokenVerifier extension point (sessions' r.requires target)sessions → revocable JWTscrypto-shredding → operator-triggered subject-key eraserate-limiting → ops-side rate-limit status queryaudit → tenant-scoped audit trailWhen to reach for it
Section titled “When to reach for it”Any app that also mounts composeGdprStack({ selfService }), which already
mounts sessions as part of its own require-chain. Combine both directly;
dedupeFeatures() collapses the two identical sessions instances at boot:
import { composeGdprStack, composeSecurityStack,} from "@cosmicdrift/kumiko-dev-server/compose-stacks";
export const APP_FEATURES = [ // ...config, user, tenant, auth-foundation... ...composeGdprStack({ selfService: {} }), ...composeSecurityStack(),];For an app without the self-service stack, use composeSecurityStack() with
its default options, as this recipe does.
Dedupe keeps the first mounted instance. Late-bound state such as
bindAutoRevokeOnPasswordChange must be bound on the instance returned by
composeFeatures/createApp, not on a locally held handle to the dropped one:
the dropped instance’s hooks never run and password changes silently stop
revoking sessions.
A createSessionsFeature({ ... }) mounted with different options than the
stack’s own no-arg instance still produces a clear boot error — dedupe only
collapses provably interchangeable instances, never silently picks one:
export const APP_FEATURES = [ // ... createSessionsFeature({ expiresInMs: 1000 }), ...composeSecurityStack(), // throws: Duplicate feature: "sessions" mounted twice with different // options — mount it once or pass identical options];bun test samples/recipes/security-baseline/src/__tests__/feature.test.tsThree cases: APP_FEATURES boots clean; at NODE_ENV=production it produces
no security-baseline warning; dropping crypto-shredding/rate-limiting/
audit (sessions stays mounted — auth-foundation’s own bootCheck requires
a sessionStore) at NODE_ENV=production produces exactly one warning and
still does not throw.
Source code
Section titled “Source code”The feature entry point — embedded straight from the source file, so the code here is exactly what runs. Multi-file samples keep their remaining files next to it on GitHub (link below):
// Security-Baseline Sample// Shows: mounting composeSecurityStack() (sessions, crypto-shredding,// rate-limiting, audit) and how it silences the NODE_ENV=production boot// warning that fires when one of those four is missing.
import { createAuthFoundationFeature } from "@cosmicdrift/kumiko-bundled-features/auth-foundation";import { createConfigFeature } from "@cosmicdrift/kumiko-bundled-features/config";import { createTenantFeature } from "@cosmicdrift/kumiko-bundled-features/tenant";import { createUserFeature } from "@cosmicdrift/kumiko-bundled-features/user";import { composeSecurityStack } from "@cosmicdrift/kumiko-dev-server/compose-stacks";import type { FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
export const APP_FEATURES: FeatureDefinition[] = [ createConfigFeature(), createUserFeature(), createTenantFeature(), createAuthFoundationFeature(), ...composeSecurityStack(),];📄 On GitHub: samples/recipes/security-baseline/src/feature.ts