@cosmicdrift/kumiko-dev-server 0.389.0 → 0.390.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,7 +59,8 @@ const hasBun = typeof Bun !== "undefined";
59
59
  // reference the package at runtime (no native binding, plain HTTP client,
60
60
  // but a real runtime import). A production crash in money-horse,
61
61
  // "Cannot find package 'meilisearch'", occurred because it used to be in
62
- // BUILD_ONLY_EXTERNALS instead.
62
+ // BUILD_ONLY_EXTERNALS instead. Its version comes from the framework's
63
+ // peerDependencies range.
63
64
  const RUNTIME_EXTERNALS = [
64
65
  "@node-rs/argon2",
65
66
  "sharp",
@@ -296,7 +297,8 @@ async function resolveRuntimeDepsVersions(cwd, repoRoot, packages) {
296
297
  continue;
297
298
  const raw = await readFile(path, "utf-8");
298
299
  const parsed = parseJsonOrThrow(raw, path);
299
- Object.assign(allDeps, parsed.dependencies ?? {}, parsed.optionalDependencies ?? {});
300
+ // Peers first: a real dependency of the same source overrides a peer range.
301
+ Object.assign(allDeps, parsed.peerDependencies ?? {}, parsed.dependencies ?? {}, parsed.optionalDependencies ?? {});
300
302
  }
301
303
  // App package.json fills gaps only — pins extraRuntimeExternals without
302
304
  // overriding framework-native pins the app may also list.
@@ -39,6 +39,7 @@ function buildDevStackOptions(options, dbMode) {
39
39
  ...(options.extraRoutes !== undefined && { extraRoutes: options.extraRoutes }),
40
40
  ...(options.trustedProxyHops !== undefined && { trustedProxyHops: options.trustedProxyHops }),
41
41
  ...(options.errorDocs && { errorDocs: options.errorDocs }),
42
+ ...(options.rowLevelSecurity !== undefined && { rowLevelSecurity: options.rowLevelSecurity }),
42
43
  // jobs.consumerLane unset = enqueuer-only; startDevJobRunners is the sole
43
44
  // consumer/cron-scheduler per lane, so runOnBoot/cron jobs don't
44
45
  // double-fire.
@@ -48,7 +49,7 @@ function buildDevStackOptions(options, dbMode) {
48
49
  export async function bootDevStack(options, dbMode) {
49
50
  logInfo(`[kumiko-server] booting Kumiko stack${dbMode.persistentDb ? ` — persistent DB "${dbMode.devDbName}"` : " — ephemeral test DB"}…`);
50
51
  const stack = await setupTestStack(buildDevStackOptions(options, dbMode));
51
- await createEventsTable(stack.db);
52
+ await createEventsTable(stack.unscopedDb);
52
53
  await pushEntityProjectionTables(stack, stack.registry);
53
54
  // `extraRoutes` is already mounted by setupTestStack's buildServer();
54
55
  // `wire` is for co-running components that need the system-write
@@ -86,13 +87,13 @@ export async function startDevBackgroundRunners(stack, jobQueueNamePrefix) {
86
87
  registry: stack.registry,
87
88
  db: stack.db,
88
89
  // The stack's own AppContext, not a `{ db, registry }` literal: these
89
- // runners consume the lanes, and a job reaching for ctx.files /
90
+ // runners consume the lanes, and a job reaching for ctx.services.files /
90
91
  // ctx.notify / ctx.config would otherwise die here while the very same
91
92
  // handler works on the request path.
92
93
  context: stack.context,
93
94
  redisUrl,
94
95
  // Same dispatcher the request path writes through — without this,
95
- // ctx.write/ctx.writeAs/ctx.queryAs inside a dev-run job throw on their
96
+ // ctx.dispatch.write/ctx.dispatch.writeAs/ctx.dispatch.queryAs inside a dev-run job throw on their
96
97
  // first call.
97
98
  dispatcher: stack.dispatcher,
98
99
  // Must match setupTestStack's jobs.queueNamePrefix so enqueuer and
@@ -1,5 +1,6 @@
1
1
  /// <reference types="bun-types" preserve="true" />
2
2
  import type { AuthRoutesConfig, ExtraRouteDefinition } from "@cosmicdrift/kumiko-framework/api";
3
+ import { type RowLevelSecurityMode } from "@cosmicdrift/kumiko-framework/db";
3
4
  import { type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
4
5
  import type { ErrorDocsConfig } from "@cosmicdrift/kumiko-framework/errors";
5
6
  import type { PageHeadResolver, PageHeadSystemQuery } from "@cosmicdrift/kumiko-renderer/headless/apex";
@@ -64,6 +65,8 @@ export type DevHostDispatch = (req: Request, deps: {
64
65
  readonly cspScriptHashes: readonly string[];
65
66
  }) => DevHostDispatchResult | Promise<DevHostDispatchResult>;
66
67
  export type CreateKumikoServerOptions = {
68
+ /** Row level security of the dev stack, as in prod. Default "on", so tenant-isolation bugs show up in development. */
69
+ readonly rowLevelSecurity?: RowLevelSecurityMode;
67
70
  /** Features whose entities, handlers, and screens get wired into the
68
71
  * dev stack. Pass every feature the app is supposed to run. */
69
72
  readonly features: readonly FeatureDefinition[];
@@ -141,7 +144,7 @@ export type CreateKumikoServerOptions = {
141
144
  readonly anonymousAccess?: TestStackOptions["anonymousAccess"];
142
145
  /** File-storage provider forwarded to `setupTestStack`. When set, the
143
146
  * stack mounts the upload routes (POST/GET/DELETE /api/files), exposes
144
- * `ctx.files`, and creates the `file_refs` table. Example:
147
+ * `ctx.services.files`, and creates the `file_refs` table. Example:
145
148
  * `{ storageProvider: createInMemoryFileProvider() }`. */
146
149
  readonly files?: TestStackOptions["files"];
147
150
  /** Feature-toggle resolver forwarded to `setupTestStack`. When set, the
@@ -14,6 +14,7 @@
14
14
  //
15
15
  // The companion prod entry will land at `@cosmicdrift/kumiko-framework/server`
16
16
  // with a different options shape (clientDist, auth config, db url).
17
+ import { assertNoActiveRowLevelSecurityScope, } from "@cosmicdrift/kumiko-framework/db";
17
18
  import { buildAppSchema } from "@cosmicdrift/kumiko-framework/engine";
18
19
  import { buildBunServeOptions, resolveDerivedMaxRequestBodySize, } from "@cosmicdrift/kumiko-server-runtime/bun-serve-options";
19
20
  import { stripNoRouteMatchHeader } from "@cosmicdrift/kumiko-server-runtime/try-hono-first";
@@ -33,6 +34,7 @@ import { hasBun, logInfo } from "./dev-server-runtime.js";
33
34
  function startDevHttpServer(port, handleFetch, stack) {
34
35
  if (!hasBun)
35
36
  return undefined;
37
+ assertNoActiveRowLevelSecurityScope("dev server");
36
38
  return Bun.serve(buildBunServeOptions(port, handleFetch, resolveDerivedMaxRequestBodySize(stack.registry), {
37
39
  upgradeFetch: async (req, env) => stripNoRouteMatchHeader(await stack.app.fetch(req, env)),
38
40
  }));
@@ -1,4 +1,4 @@
1
- import { type FeaturePattern, type ParseError } from "@cosmicdrift/kumiko-framework/engine";
1
+ import { type FeaturePattern, type ParseError } from "@cosmicdrift/kumiko-feature-ast";
2
2
  export type AuthoringStyle = "canonical" | "legacy";
3
3
  export type CorpusWarning = {
4
4
  /** Repo-relative path to the file that triggered the warning. */
@@ -31,7 +31,7 @@
31
31
  // builder is permissive, and the Designer filters at read-time.
32
32
  import { existsSync, readdirSync, readFileSync } from "node:fs";
33
33
  import { dirname, join, relative, resolve } from "node:path";
34
- import { PATTERN_LIBRARY, parseFeatureFile, } from "@cosmicdrift/kumiko-framework/engine";
34
+ import { PATTERN_LIBRARY, parseFeatureFile, } from "@cosmicdrift/kumiko-feature-ast";
35
35
  import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
36
36
  // =============================================================================
37
37
  // Builder
@@ -101,7 +101,7 @@ export function renderManagedTemplates(facts) {
101
101
  });
102
102
  return [
103
103
  ...deploy.files
104
- // Managed apps migrate through the k3s release.yml InitContainer; migrate-step.sh is compose-only.
104
+ // Managed apps migrate through the migrate-db initContainer of the infra _app-deploy.yml; migrate-step.sh is compose-only.
105
105
  .filter((file) => file.output !== COMPOSE_ONLY_DEPLOY_FILE)
106
106
  .map((file) => marked(`deploy/${file.output}`, file.content, "#", file.output.endsWith(".sh"))),
107
107
  marked(".husky/pre-push", readTemplate("husky-pre-push.template"), "#", true),
@@ -3,6 +3,7 @@ function pickDevClientOptions(options) {
3
3
  return {
4
4
  ...(options.clientEntry !== undefined && { clientEntry: options.clientEntry }),
5
5
  ...(options.errorDocs && { errorDocs: options.errorDocs }),
6
+ ...(options.rowLevelSecurity !== undefined && { rowLevelSecurity: options.rowLevelSecurity }),
6
7
  ...(options.clientEntries !== undefined && { clientEntries: options.clientEntries }),
7
8
  ...(options.hostDispatch !== undefined && { hostDispatch: options.hostDispatch }),
8
9
  ...(options.resolvePageHead !== undefined && { resolvePageHead: options.resolvePageHead }),
@@ -1,6 +1,7 @@
1
1
  import type { SeedAdminOptions } from "@cosmicdrift/kumiko-bundled-features/auth-email-password/seeding";
2
2
  import type { PostAuthLandingResolver } from "@cosmicdrift/kumiko-framework/api";
3
3
  import type { KmsAdapter } from "@cosmicdrift/kumiko-framework/crypto";
4
+ import type { RowLevelSecurityMode } from "@cosmicdrift/kumiko-framework/db";
4
5
  import { type BundledListsOptions, type FeatureDefinition, type Registry, type ValidateBootOptions } from "@cosmicdrift/kumiko-framework/engine";
5
6
  import type { ComposedEnvSchema } from "@cosmicdrift/kumiko-framework/env";
6
7
  import type { ErrorDocsConfig } from "@cosmicdrift/kumiko-framework/errors";
@@ -128,7 +129,7 @@ export type RunDevAppOptions = {
128
129
  readonly extraContext?: CreateKumikoServerOptions["extraContext"];
129
130
  /** App-level config values, same semantics and merge as RunProdAppOptions["appOverrides"]. */
130
131
  readonly appOverrides?: ConfigOverridesInput;
131
- /** MasterKeyProvider for the automatically wired `ctx.secrets`. Defaults to
132
+ /** MasterKeyProvider for the automatically wired `ctx.services.secrets`. Defaults to
132
133
  * `createEnvMasterKeyProvider`; override it for KMS backends. Only relevant
133
134
  * when the secrets feature is mounted. Symmetric with runProdApp. */
134
135
  readonly masterKey?: MasterKeyProvider;
@@ -153,7 +154,7 @@ export type RunDevAppOptions = {
153
154
  * role in `access.roles`. */
154
155
  readonly anonymousAccess?: CreateKumikoServerOptions["anonymousAccess"];
155
156
  /** File-Storage-Provider — aktiviert die Upload-Routes (/api/files) +
156
- * `ctx.files`. Demos: `{ storageProvider: createInMemoryFileProvider() }`. */
157
+ * `ctx.services.files`. Demos: `{ storageProvider: createInMemoryFileProvider() }`. */
157
158
  readonly files?: CreateKumikoServerOptions["files"];
158
159
  /** App-defined HTTP routes, such as /feed.xml and /sitemap.xml. They are
159
160
  * attached to the Hono app before the static-asset path. Symmetric with the
@@ -202,6 +203,8 @@ export type RunDevAppOptions = {
202
203
  readonly trustedProxyHops?: number;
203
204
  /** Links the app's own error reasons to its own docs (`docsUrl` in error responses); framework reasons keep the framework docs link. */
204
205
  readonly errorDocs?: ErrorDocsConfig;
206
+ /** Row level security of the dev stack, as in prod. Default "on", so tenant-isolation bugs show up in development. */
207
+ readonly rowLevelSecurity?: RowLevelSecurityMode;
205
208
  };
206
209
  export declare function runDevApp(options: RunDevAppOptions): Promise<KumikoServerHandle>;
207
210
  export declare function mergeConfigResolverDefault(ctx: CreateKumikoServerOptions["extraContext"], registry: Registry, envSource: Record<string, string | undefined>, cipher?: EnvelopeCipher, appOverrides?: ConfigOverridesInput): CreateKumikoServerOptions["extraContext"];
@@ -21,6 +21,7 @@ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
21
21
  import { dirname, join, resolve } from "node:path";
22
22
  import { APP_CONFIG_FILE } from "@cosmicdrift/kumiko-framework/config";
23
23
  import { collectTableMetas, generateMigration, writeSnapshotJson, } from "@cosmicdrift/kumiko-framework/db";
24
+ import { DEFAULT_ROW_LEVEL_SECURITY_MODE } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
24
25
  import { escapeHtml } from "@cosmicdrift/kumiko-renderer/headless";
25
26
  import { composeFeatures } from "@cosmicdrift/kumiko-server-runtime/compose-features";
26
27
  import { resolveFrameworkVersion } from "./framework-version.js";
@@ -396,6 +397,7 @@ const kmsWiring =
396
397
  await runProdApp({
397
398
  features: APP_FEATURES,
398
399
  ...(appConfig.validateBootOptions && { validateBootOptions: appConfig.validateBootOptions }),
400
+ ...(appConfig.rowLevelSecurity && { rowLevelSecurity: appConfig.rowLevelSecurity }),
399
401
  envSchema,
400
402
  staticDir: "./dist",
401
403
  ...kmsWiring,
@@ -412,12 +414,9 @@ function renderServer(appName) {
412
414
  { specifier: "../kumiko.config", names: [], defaultName: "appConfig" },
413
415
  { specifier: "../src/seed", names: ["seedDemoTasks"] },
414
416
  ]);
415
- // Dev-only emailVerified skips the login verify gate for the seeded admin;
416
- // prod's bin/main.ts leaves it unset because the verify flow must run there.
417
417
  const authBlock = renderAdminAuthBlock(appName, [
418
418
  'password: "changeme",',
419
419
  'displayName: "Admin",',
420
- "emailVerified: true,",
421
420
  ]);
422
421
  return `// Dev bootstrap. \`bun --watch bin/server.ts\` (see package.json scripts.dev)
423
422
  // starts a full-featured dev server with auto-reload on code changes.
@@ -715,21 +714,22 @@ function renderBinKumiko() {
715
714
  "// consumer status|restart <name>` to recover a dead event consumer without",
716
715
  "// ad-hoc SQL. kumiko-build bundles this file to dist-server/kumiko.js.",
717
716
  "",
718
- 'import { allAppFeatures } from "@cosmicdrift/kumiko-framework/config";',
719
717
  'import { runConsumerCli } from "@cosmicdrift/kumiko-framework/consumer-cli";',
720
718
  'import { kmsSlotsOf } from "@cosmicdrift/kumiko-framework/env";',
721
719
  'import { runSchemaCli } from "@cosmicdrift/kumiko-framework/schema-cli";',
722
- 'import appConfig from "../kumiko.config";',
720
+ 'import { FEATURES } from "../kumiko/schema";',
723
721
  'import { envSchema } from "./env-schema";',
724
722
  "",
725
723
  "const [, , cmd, ...rest] = Bun.argv;",
726
724
  "const out = { log: (l: string) => console.log(l), err: (l: string) => console.error(l) };",
727
725
  "",
728
726
  'if (cmd === "schema") {',
729
- " const features = allAppFeatures(appConfig);",
730
727
  " const kmsSlots = kmsSlotsOf(envSchema.schema);",
731
728
  " process.exit(",
732
- " await runSchemaCli(rest, process.env.INIT_CWD ?? process.cwd(), out, { features, kmsSlots }),",
729
+ " await runSchemaCli(rest, process.env.INIT_CWD ?? process.cwd(), out, {",
730
+ " features: FEATURES,",
731
+ " kmsSlots,",
732
+ " }),",
733
733
  " );",
734
734
  "}",
735
735
  "",
@@ -774,6 +774,7 @@ async function writeInitMigration(destination, features) {
774
774
  const metas = collectTableMetas(composed);
775
775
  const result = generateMigration({
776
776
  metas,
777
+ rowLevelSecurity: DEFAULT_ROW_LEVEL_SECURITY_MODE,
777
778
  prevSnapshot: null,
778
779
  name: "init",
779
780
  sequenceNumber: 1,
@@ -12,7 +12,7 @@
12
12
  // scaffolding).
13
13
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
14
14
  import { join, resolve } from "node:path";
15
- import { renderFeatureFile, } from "@cosmicdrift/kumiko-framework/engine";
15
+ import { renderFeatureFile, } from "@cosmicdrift/kumiko-feature-ast";
16
16
  /**
17
17
  * Generate a starter feature workspace at `destination`. Throws when
18
18
  * the destination already exists — refuses to overwrite. The caller is
@@ -1,3 +1,4 @@
1
+ import { type RowLevelSecurityMode } from "@cosmicdrift/kumiko-framework/db";
1
2
  import { type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
2
3
  import { type ComposeFeaturesOptions } from "@cosmicdrift/kumiko-server-runtime/compose-features";
3
4
  export type SchemaApplyOptions = ComposeFeaturesOptions & {
@@ -6,6 +7,8 @@ export type SchemaApplyOptions = ComposeFeaturesOptions & {
6
7
  readonly features: readonly FeatureDefinition[];
7
8
  /** Default INIT_CWD ?? process.cwd(); migrations live under <appCwd>/kumiko/migrations. */
8
9
  readonly appCwd?: string;
10
+ /** Pass appConfig.rowLevelSecurity: on a role the policies bind, migrations and rebuilds only see rows through an "on" connection. Default "on". */
11
+ readonly rowLevelSecurity?: RowLevelSecurityMode;
9
12
  };
10
13
  export declare function runSchemaApply(opts: SchemaApplyOptions): Promise<number>;
11
14
  export declare function runStandaloneSchemaCli(opts: SchemaApplyOptions): Promise<never>;
@@ -7,9 +7,10 @@
7
7
  // kumiko-build discovers the app bin via findRepoRoot() and bundles it.
8
8
  import { existsSync } from "node:fs";
9
9
  import { join } from "node:path";
10
- import { createDbConnection, runMigrationsFromDir } from "@cosmicdrift/kumiko-framework/db";
10
+ import { createDbConnection, runMigrationsFromDir, } from "@cosmicdrift/kumiko-framework/db";
11
11
  import { createRegistry } from "@cosmicdrift/kumiko-framework/engine";
12
12
  import { createEventsTable } from "@cosmicdrift/kumiko-framework/event-store";
13
+ import { DEFAULT_ROW_LEVEL_SECURITY_MODE } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
13
14
  import { queueRebuildsFromMarkers, reportPendingRebuildRun, runPendingRebuilds, } from "@cosmicdrift/kumiko-framework/migrations";
14
15
  import { createEventConsumerStateTable, createProjectionStateTable, } from "@cosmicdrift/kumiko-framework/pipeline";
15
16
  import { composeFeatures, } from "@cosmicdrift/kumiko-server-runtime/compose-features";
@@ -25,7 +26,10 @@ export async function runSchemaApply(opts) {
25
26
  console.error(`\n ${migrationsDir} is missing — kumiko/migrations/ must be part of the image.\n`);
26
27
  return 1;
27
28
  }
28
- const { db, close } = createDbConnection({ url: dbUrl });
29
+ const { db, close } = createDbConnection({
30
+ url: dbUrl,
31
+ rowLevelSecurity: opts.rowLevelSecurity ?? DEFAULT_ROW_LEVEL_SECURITY_MODE,
32
+ });
29
33
  try {
30
34
  // Create infrastructure tables (event store + pipeline state) first; all are
31
35
  // tableExists-gated and idempotent: on an existing DB this is a no-op, while
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-dev-server",
3
- "version": "0.389.0",
3
+ "version": "0.390.0",
4
4
  "description": "Dev-tooling for Kumiko apps: local dev-server bootstrap (runDevApp), scaffolding. Its compose-stacks/env-schema subpaths are consumed at prod boot too — see @cosmicdrift/kumiko-server-runtime for the prod runner itself.",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -77,14 +77,15 @@
77
77
  "kumiko-upgrade": "./bin/kumiko-upgrade.ts"
78
78
  },
79
79
  "dependencies": {
80
- "@cosmicdrift/kumiko-bundled-features": "0.389.0",
81
- "@cosmicdrift/kumiko-framework": "0.389.0",
82
- "@cosmicdrift/kumiko-renderer": "0.389.0",
83
- "@cosmicdrift/kumiko-renderer-mail-html": "0.389.0",
84
- "@cosmicdrift/kumiko-guards": "0.389.0",
85
- "@cosmicdrift/kumiko-server-runtime": "0.389.0",
86
- "@cosmicdrift/kumiko-testing": "0.389.0",
87
- "@cosmicdrift/kumiko-types": "0.389.0",
80
+ "@cosmicdrift/kumiko-bundled-features": "0.390.0",
81
+ "@cosmicdrift/kumiko-feature-ast": "0.390.0",
82
+ "@cosmicdrift/kumiko-framework": "0.390.0",
83
+ "@cosmicdrift/kumiko-renderer": "0.390.0",
84
+ "@cosmicdrift/kumiko-renderer-mail-html": "0.390.0",
85
+ "@cosmicdrift/kumiko-guards": "0.390.0",
86
+ "@cosmicdrift/kumiko-server-runtime": "0.390.0",
87
+ "@cosmicdrift/kumiko-testing": "0.390.0",
88
+ "@cosmicdrift/kumiko-types": "0.390.0",
88
89
  "ts-morph": "^28.0.0",
89
90
  "zod": "^4.4.3"
90
91
  },
package/src/changes.json CHANGED
@@ -1,4 +1,30 @@
1
1
  [
2
+ {
3
+ "version": "0.390.0",
4
+ "type": "fix",
5
+ "title": "The managed Dockerfile names the real k3s migration step",
6
+ "detail": "The comment above the migrations COPY in the managed Dockerfile pointed at a release.yml InitContainer that does not exist. It now names the migrate-db initContainer of the infra _app-deploy.yml, and migrate-step.sh for compose hosts. Only the comment changes; the next kumiko-upgrade shows it as a managed-file diff."
7
+ },
8
+ {
9
+ "version": "0.390.0",
10
+ "type": "fix",
11
+ "title": "Scaffolded bin/kumiko.ts passes FEATURES from kumiko/schema",
12
+ "detail": "The scaffolded standalone CLI handed allAppFeatures(appConfig) to runSchemaCli, a raw list without the bundled foundation. It now imports FEATURES from kumiko/schema, which the Schema CLI Features guard requires. Existing apps are not touched."
13
+ },
14
+ {
15
+ "version": "0.390.0",
16
+ "type": "breaking",
17
+ "title": "The dev server runs with row level security on",
18
+ "detail": "runDevApp and createKumikoServer default to rowLevelSecurity \"on\" and run the dev stack as kumiko_app. On boot against a persistent dev database, the stack creates the role and transfers ownership of the database and of the public schema's tables, views and sequences to kumiko_app. The database user in DATABASE_URL needs the right to create roles, which the superuser of a local Postgres has. Code that reads tenant tables through a raw connection without a scope sees no rows in development; the boot does not detect this. runSchemaApply takes rowLevelSecurity, and the managed kumiko/schema.ts exports ROW_LEVEL_SECURITY from appConfig; the managed file updates on the next sync.",
19
+ "migration": "Pass rowLevelSecurity: \"off\" to runDevApp while fixing code paths without tenant context; the dev database keeps kumiko_app as owner. A server entry written by an older scaffold passes appConfig.rowLevelSecurity to runProdApp by hand once the app turns row level security on. Manual migration, there is no codemod."
20
+ },
21
+ {
22
+ "version": "0.390.0",
23
+ "type": "improvement",
24
+ "title": "Scaffolded dev admin leaves emailVerified to the boot seed",
25
+ "detail": "The boot seed marks the operator-created admin and the seed shortcut accounts as email-verified, so the scaffolded `bin/server.ts` no longer sets `emailVerified: true` on its own. Self-registered users still go through the verify flow.",
26
+ "migration": "Optional. Existing apps can drop `emailVerified: true` from `auth.admin` in `bin/server.ts` and `bin/main.ts`; an explicit value still wins over the default."
27
+ },
2
28
  {
3
29
  "version": "0.389.0",
4
30
  "type": "improvement",
@@ -70,7 +70,7 @@ RUN bun install --production --frozen-lockfile
70
70
  COPY --from=build --chown=app:app /app/dist ./dist
71
71
 
72
72
  # Checked-in SQL migrations for the pre-deploy `kumiko schema apply` step
73
- # (k3s InitContainer from release.yml, or migrate-step.sh on compose hosts).
73
+ # (migrate-db initContainer of the infra _app-deploy.yml on k3s, or migrate-step.sh on compose hosts).
74
74
  # The shipped `kumiko-schema` bin reads `${INIT_CWD}/kumiko/migrations/*.sql`
75
75
  # (see ENV INIT_CWD=/app below). kumiko/schema.ts is NOT needed at runtime
76
76
  # (only generate reads it; apply is checksum-tracked SQL only).
@@ -28,4 +28,7 @@ export const FEATURES: readonly FeatureDefinition[] = composeFeatures([...CONFIG
28
28
  // `kumiko check` validates the boot with the options kumiko.config.ts declares.
29
29
  export const VALIDATE_BOOT_OPTIONS = appConfig.validateBootOptions;
30
30
 
31
+ // Decides whether `generate` emits row-level-security policies and `apply` runs as a policy-enforced connection.
32
+ export const ROW_LEVEL_SECURITY = appConfig.rowLevelSecurity;
33
+
31
34
  export const ENTITY_METAS: readonly EntityTableMeta[] = collectTableMetas(FEATURES);