Skip to content

secrets

Stores arbitrary per-tenant secrets (API keys, tokens, credentials) encrypted at rest using AES-256 with a KEK loaded from KUMIKO_SECRETS_MASTER_KEY_V1 (and successive versions for rotation). Read a secret in handlers via ctx.secrets.get(tenantId, handle), which automatically appends a tenantSecretRead audit event so every access is traceable. A rotate job re-encrypts all envelopes after a KEK version bump.

From recipes-managed-config — the smallest working mount:

// 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

What this feature needs to run (Requires, top) and the write commands it provides (Provides, bottom).

flowchart TB
  n_secrets["secrets"]
  subgraph how_provides["Provides"]
    n_cmd_secrets_write_delete(["delete"])
    n_cmd_secrets_write_set(["set"])
  end
  n_secrets --> n_cmd_secrets_write_delete
  n_secrets --> n_cmd_secrets_write_set

Provides — write commands this feature registers (dispatch them through the command bus):

Start with recipes-managed-config for a step-by-step walkthrough with runnable code and integration tests.

  • Requires: none
  • Activation: always on (not toggleable)