Skip to content

Recipe: Managed config

What this shows: how one declarative config surface provisions everything a key needs — storage, masking, cascade, and a settings-hub entry — across two backings and two scopes, without a hand-written screen, nav, or env-wiring map.

Two keys, deliberately different:

KeyScopeBackingStory
payment-api-keysystemsecretsPlatform-owned secret (one Stripe key bills all tenants). Stored envelope-encrypted in the secrets store, masked in every query, revealed only for the owning feature’s ctx.config read.
smtp-hosttenantconfigPlatform default that a tenant admin overrides. Cascade resolves tenant-row → system-row → default; one tenant’s override never leaks to another.
import { access, createSystemConfig, createTenantConfig } from "@cosmicdrift/kumiko-framework/engine";
r.config({
keys: {
// System-only secret: storage routes to the secrets envelope (KEK rotation
// + audit-on-read), never config_values. backing:"secrets" is system-only —
// the boot-guard rejects it on tenant/user scope (secrets has no cascade).
"payment-api-key": createSystemConfig("text", {
backing: "secrets",
write: access.systemAdmin,
read: access.admin,
mask: { title: "integrations.payment-api-key", icon: "credit-card", order: 1 },
}),
// Tenant override with a platform default sourced from an env var at boot.
"smtp-host": createTenantConfig("text", {
env: "SMTP_HOST",
default: "smtp.platform.example",
write: access.roles("SystemAdmin", "Admin"),
read: access.admin,
mask: { title: "integrations.smtp-host", icon: "mail", order: 2 },
}),
},
});
  • backing: "secrets" — the value lives in the secrets store (envelope encryption, KEK rotation, audit-on-read) instead of config_values. Set/read/ reset dispatch through ctx.secrets; the query handlers mask the value while the owning feature still reads the revealed plaintext via ctx.config. Only valid for scope: "system" — secrets are flat per (tenant, key) with no cascade, so a tenant override would be incoherent and is rejected at boot.
  • env: "SMTP_HOST" — at boot (runProdApp) the env var seeds the key’s default as an app-override, below any system-row. The env var is the default; no manual AppConfigOverrides map, no re-typing in the admin UI.
  • mask — the key surfaces automatically in the self-populating settings hub: buildConfigFeatureSchema derives a configEdit screen + a child nav under the audience hub (Platform / Organisation / Personal) from the key’s scope and type. No r.screen, no r.nav. mask.title is the i18n label key.
import {
createConfigAccessorFactory,
createConfigResolver,
} from "@cosmicdrift/kumiko-bundled-features/config";
import { createSecretsContext } from "@cosmicdrift/kumiko-bundled-features/secrets";
import { createEnvMasterKeyProvider } from "@cosmicdrift/kumiko-framework/secrets";
const masterKeyProvider = createEnvMasterKeyProvider({ env: process.env });
const resolver = createConfigResolver();
await runProdApp({
extraContext: ({ db, registry }) => ({
configResolver: resolver,
_configAccessorFactory: createConfigAccessorFactory(registry, resolver),
// Required whenever a key declares backing:"secrets" — without it the set
// and read paths fail loud, never silently miss.
secrets: createSecretsContext({ db, masterKeyProvider }),
}),
// ...
});

Register the secrets feature too so its tenant_secrets table is migrated; the config backing only needs the ctx.secrets context shown above.

PatternStorageCascadeUse case
encrypted: true config key (encrypted-tenant-config)config_values, AES ciphertextsystem → tenantPer-tenant customer secret, no KEK rotation/audit.
backing: "secrets" config key (this recipe)secrets envelope, system tenantnone (system-only)Platform-owned secret needing rotation + audit-on-read, declared as a config key.
r.secret (secrets-demo)secrets envelopenoneSame storage, but addressed directly — no config key, no settings-hub entry.

backing: "secrets" is the bridge: you get the secrets store’s guarantees and the config feature’s declarative surface (masking, settings hub, the same config:write:set write path).

  1. SystemAdmin sets payment-api-key (backing: "secrets") → value lands in the secrets envelope, masked in every query.
  2. Owning feature reads plaintext via ctx.config — audit-on-read from secrets.
  3. SystemAdmin sets platform smtp-host default (system scope).
  4. Tenant A overrides smtp-host → cascade resolves tenant-row for A only.
  5. Tenant B still sees the system default — no cross-tenant leak.
Terminal window
bun test src/__tests__/feature.integration.test.ts

Proves:

  • backing: "secrets" routes to secrets store, not config_values
  • Owning feature reads revealed plaintext; cascade queries stay masked
  • Tenant SMTP override wins locally and never leaks to another tenant
  • Boot rejects integrationsFeature without the secrets feature mounted
  • Tenant-scoped secrets — forbidden by declaration. backing: "secrets" + scope: "tenant"/"user" fails at boot; use encrypted: true for a cascading per-tenant secret.
  • The env→default bridge at runtimeenv: is wired by runProdApp, not by setupTestStack, so the integration test sets the platform default explicitly via a scope: "system" write rather than through the env var.

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):

// Managed config — one declaration provisions everything a config key needs.
//
// Two keys, two backings, two scopes — same declarative surface:
//
// payment-api-key scope:"system" backing:"secrets" platform-owned secret
// → stored envelope-encrypted in the secrets store (system tenant), masked
// in every query, revealed only for the owning feature's ctx.config read.
//
// smtp-host scope:"tenant" (config, plain) per-tenant override
// → platform default (env/system-row) that a tenant admin can override;
// cascade resolves tenant-row → system-row → default.
//
// `mask` makes each key surface in the self-populating settings hub without a
// hand-written r.screen / r.nav. `env` wires the platform default from an
// environment variable at boot (runProdApp) — no manual AppConfigOverrides map.
import {
access,
type ConfigKeyHandle,
createSystemConfig,
createTenantConfig,
defineFeature,
} from "@cosmicdrift/kumiko-framework/engine";
import { z } from "zod";
const FEATURE = "integrations";
export const paymentApiKeyHandle: ConfigKeyHandle<"text"> = {
name: `${FEATURE}:config:payment-api-key`,
type: "text",
};
export const smtpHostHandle: ConfigKeyHandle<"text"> = {
name: `${FEATURE}:config:smtp-host`,
type: "text",
};
export const integrationsFeature = defineFeature(FEATURE, (r) => {
r.requires("config");
// backing:"secrets" stores payment-api-key in the secrets envelope, which
// the secrets feature provisions — declare the dependency so registry-build
// enforces it instead of leaving a consumer to discover the missing
// tenant_secrets table at runtime.
r.requires("secrets");
r.config({
keys: {
// System-only secret: backing:"secrets" routes storage to the secrets
// envelope (KEK rotation + audit-on-read), never config_values. The
// boot-guard rejects backing:"secrets" on any non-system scope.
"payment-api-key": createSystemConfig("text", {
backing: "secrets",
write: access.systemAdmin,
read: access.admin,
mask: { title: "integrations.payment-api-key", icon: "credit-card", order: 1 },
}),
// Tenant override with a platform default. env seeds the default from
// SMTP_HOST at boot; a tenant admin overrides it per tenant.
"smtp-host": createTenantConfig("text", {
env: "SMTP_HOST",
default: "smtp.platform.example",
write: access.roles("SystemAdmin", "Admin"),
read: access.admin,
mask: { title: "integrations.smtp-host", icon: "mail", order: 2 },
}),
},
});
// Internal-read probe: the owning feature reads its own secrets-backed key
// via ctx.config and receives the revealed plaintext, never the mask.
r.queryHandler(
"peek-payment-key",
z.object({}),
async (_query, ctx) => {
if (!ctx.config) throw new Error("ctx.config not wired");
return { value: await ctx.config(paymentApiKeyHandle) };
},
{ access: { roles: ["SystemAdmin"] } },
);
r.translations({
keys: {
"integrations.settings": { de: "Integrationen", en: "Integrations" },
"integrations.payment-api-key": { de: "Zahlungs-API-Schlüssel", en: "Payment API Key" },
"integrations.smtp-host": { de: "SMTP-Server", en: "SMTP Host" },
},
});
});

📄 On GitHub: samples/recipes/managed-config/src/feature.ts