@voltro/cli 0.36.0 → 0.37.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/apiBuild-D0f6hYbH.js +2 -0
  3. package/dist/{apiBuild-DiE9zcj6.js → apiBuild-DGsShRuF.js} +1 -1
  4. package/dist/{baselineCommand-DIttzO8A.js → baselineCommand-C6NMt-oa.js} +1 -4
  5. package/dist/baselineCommand-Cfw2Afwm.js +2 -0
  6. package/dist/bin.js +1 -1
  7. package/dist/{build-DBe6c7qC.js → build-CiFA-E19.js} +7 -7
  8. package/dist/{checkCommand-BTA6P3Yr.js → checkCommand-CIPYyNyc.js} +1 -1
  9. package/dist/{checkCommand-BhG0gmUD.js → checkCommand-CLPTzYqM.js} +3 -3
  10. package/dist/{cloudCmd-Cvv5HGaZ.js → cloudCmd-NQSwe_Qk.js} +1 -1
  11. package/dist/{codegen-CYM3Zqrf.js → codegen-GYYcdpCg.js} +2 -2
  12. package/dist/codegen-uYrrDzQv.js +2 -0
  13. package/dist/{codegenCommand-DH-8ZXzR.js → codegenCommand-BxS3sCeJ.js} +2 -2
  14. package/dist/{codemodRunner-DjQGrdmg.js → codemodRunner-cyqu5oxr.js} +101 -42
  15. package/dist/{commands-Bp7utV-7.js → commands-BwG9A9Vv.js} +73 -72
  16. package/dist/{dashboardCommand-D5Jwjmr1.js → dashboardCommand-C7U146xQ.js} +1 -1
  17. package/dist/{dataCommand-DnCm0B2C.js → dataCommand-BOwd8_aJ.js} +2 -2
  18. package/dist/{dbCommand-O8HA63s2.js → dbCommand-C4UW8cVN.js} +353 -351
  19. package/dist/dbCommand-CC-qNzpz.js +2 -0
  20. package/dist/{dev-BSFTgCQ5.js → dev-BV6XNYKf.js} +1 -1
  21. package/dist/{dev-Dc4cpmRl.js → dev-bwDgSObH.js} +1 -1
  22. package/dist/{doctorCommand-CNFtt3nR.js → doctorCommand-BXyrtN2l.js} +9 -9
  23. package/dist/doctorCommand-C4L61G_L.js +2 -0
  24. package/dist/{dormancyCommand-6F6NsJKf.js → dormancyCommand-BDdRRLsa.js} +1 -1
  25. package/dist/{embeddingsCommand-DRRdneJD.js → embeddingsCommand-CjWGcAE6.js} +1 -1
  26. package/dist/{envCommand-TZzcyqZ-.js → envCommand-B_TeG297.js} +1 -1
  27. package/dist/{evolveCommand-iU35TPjT.js → evolveCommand-D6N-WfA4.js} +2 -2
  28. package/dist/index.js +2 -2
  29. package/dist/{infoCommand-unqisJGp.js → infoCommand-BHKTprrx.js} +1 -1
  30. package/dist/inspectGateHint-BF6608UT.js +4 -0
  31. package/dist/{metaCommands-7MJfZ5cf.js → metaCommands-CIoQNlaQ.js} +1 -1
  32. package/dist/{migrate-BV7I-ZHZ.js → migrate-RwgUWXfc.js} +1 -1
  33. package/dist/{privacyCommand-C-Df56U_.js → privacyCommand-XejDMvmu.js} +4 -3
  34. package/dist/{probeCommand-CZfaaUOZ.js → probeCommand-BPEpwT32.js} +40 -39
  35. package/dist/{projectScaffold-DmzEKHib.js → projectScaffold-CxgtJvlb.js} +1 -1
  36. package/dist/{projectScaffold-LMMtaavR.js → projectScaffold-DDgWbNLe.js} +2 -2
  37. package/dist/{runtimeTrace-D5LJ_N55.js → runtimeTrace-WYV7qYvz.js} +1 -1
  38. package/dist/{sdkgen-82xVuvbt.js → sdkgen-TM5sSdf7.js} +1 -1
  39. package/dist/{serveCommand-VVGcIMIw.js → serveCommand-CyZsXL1l.js} +2 -2
  40. package/dist/serveCommand-qumm7jdF.js +2 -0
  41. package/dist/serveEntry.js +1 -1
  42. package/dist/subcommandNames-CKG5Dz3a.js +27 -0
  43. package/dist/{templates-De8IR5-c.js → templates-BR-fb4SP.js} +1 -4
  44. package/dist/{test-CI6iDsYc.js → test-DIQ0jlkQ.js} +1 -1
  45. package/dist/{updateCommand-CwnUK_mf.js → updateCommand-9GHHsmDf.js} +1 -1
  46. package/dist/updateCommand-BN0tkl9-.js +2 -0
  47. package/dist/{webhooksCommand-di1CJVzu.js → webhooksCommand-CHWSPk5z.js} +1 -1
  48. package/package.json +23 -17
  49. package/templates/AGENTS.md +1 -1
  50. package/templates/agent-docs/_index.md +1 -1
  51. package/templates/agent-docs/cli.md +16 -0
  52. package/templates/agent-docs/data.md +30 -0
  53. package/templates/agent-docs/plugins/audit.md +46 -0
  54. package/templates/agent-docs/whats-new.md +56 -34
  55. package/templates/apps/api-ai/package.json +7 -7
  56. package/templates/apps/api-auth/package.json +8 -8
  57. package/templates/apps/api-backend/package.json +7 -7
  58. package/templates/apps/api-backend-deactivation/package.json +7 -7
  59. package/templates/apps/api-backend-mail/package.json +8 -8
  60. package/templates/apps/api-backend-mariadb/package.json +9 -9
  61. package/templates/apps/api-backend-sqlite/package.json +8 -8
  62. package/templates/apps/api-backend-storage/package.json +8 -8
  63. package/templates/apps/api-cms/package.json +10 -10
  64. package/templates/apps/api-collab/package.json +8 -8
  65. package/templates/apps/api-data-advanced/package.json +8 -8
  66. package/templates/apps/api-durable/package.json +8 -8
  67. package/templates/apps/api-feature-flags/package.json +9 -9
  68. package/templates/apps/api-governance/package.json +8 -8
  69. package/templates/apps/api-kv/package.json +8 -8
  70. package/templates/apps/api-moderation/package.json +8 -8
  71. package/templates/apps/api-observability/package.json +8 -8
  72. package/templates/apps/api-ratelimit/package.json +8 -8
  73. package/templates/apps/api-rbac/package.json +8 -8
  74. package/templates/apps/api-rest/package.json +7 -7
  75. package/templates/apps/api-saas/package.json +11 -11
  76. package/templates/apps/api-saas-starter/package.json +10 -10
  77. package/templates/apps/api-search/package.json +8 -8
  78. package/templates/apps/api-status/package.json +8 -8
  79. package/templates/apps/api-versioning/package.json +8 -8
  80. package/templates/apps/api-webhooks/package.json +9 -9
  81. package/templates/apps/changelog/package.json +6 -6
  82. package/templates/apps/edge-functions/package.json +2 -2
  83. package/templates/apps/frontend-admin/package.json +8 -8
  84. package/templates/apps/frontend-app/package.json +9 -9
  85. package/templates/apps/frontend-auth/package.json +8 -8
  86. package/templates/apps/frontend-blank/package.json +7 -7
  87. package/templates/apps/frontend-cms/package.json +9 -9
  88. package/templates/apps/frontend-collab/package.json +10 -10
  89. package/templates/apps/frontend-contact/package.json +7 -7
  90. package/templates/apps/frontend-dashboard/package.json +7 -7
  91. package/templates/apps/frontend-docs/package.json +7 -7
  92. package/templates/apps/frontend-i18n/package.json +6 -6
  93. package/templates/apps/frontend-landing/package.json +7 -7
  94. package/templates/apps/frontend-portal/package.json +8 -8
  95. package/templates/apps/frontend-saas/package.json +8 -8
  96. package/templates/apps/frontend-spa/package.json +7 -7
  97. package/templates/apps/frontend-ssr/package.json +7 -7
  98. package/templates/apps/frontend-ssr-api/package.json +8 -8
  99. package/templates/apps/frontend-static-blog/package.json +6 -6
  100. package/templates/apps/frontend-status/package.json +8 -8
  101. package/templates/apps/mobile-app/package.json +3 -3
  102. package/dist/apiBuild-ZEZnCXn2.js +0 -2
  103. package/dist/baselineCommand-C2ClWZN3.js +0 -2
  104. package/dist/codegen-ChBi_hVa.js +0 -2
  105. package/dist/dbCommand-DS4b97Is.js +0 -2
  106. package/dist/doctorCommand-BYffQc0s.js +0 -2
  107. package/dist/serveCommand-CEBVP36A.js +0 -2
  108. package/dist/updateCommand-CSBXVsYq.js +0 -2
@@ -0,0 +1,2 @@
1
+ import { s as e } from "./dbCommand-C4UW8cVN.js";
2
+ export { e as runDbCommand };
@@ -1,3 +1,3 @@
1
- import { c as e, d as t, f as n, i as r, u as i } from "./dev-Dc4cpmRl.js";
1
+ import { c as e, d as t, f as n, i as r, u as i } from "./dev-bwDgSObH.js";
2
2
  import "./dialectDriver-CgXnDfec.js";
3
3
  export { r as buildStore, e as loadApiConfig, i as loadDiscovered, t as runDev, n as walk };
@@ -15,7 +15,7 @@ import { t as gt } from "./manifestBuild-hpPLaGxV.js";
15
15
  import { n as _t } from "./cliRuntime-Dh7UDinH.js";
16
16
  import { n as vt, t as yt } from "./tsxLoader-EuXmSJ1K.js";
17
17
  import { n as bt } from "./agentsMd-mhQMF1bx.js";
18
- import { a as xt, c as St, o as Ct, r as wt, s as Tt } from "./codegen-CYM3Zqrf.js";
18
+ import { a as xt, c as St, o as Ct, r as wt, s as Tt } from "./codegen-GYYcdpCg.js";
19
19
  import { n as Et } from "./mssqlClusterPatch-_4cE_nun.js";
20
20
  import { p as Dt } from "./renderModeScan-D7J1B7Kw.js";
21
21
  import { i as Ot, n as kt, r as At } from "./webhookDiscovery-D7VaeMlz.js";
@@ -1,8 +1,8 @@
1
- import { Bt as e, Rt as t, zt as n } from "./dev-Dc4cpmRl.js";
1
+ import { Bt as e, Rt as t, zt as n } from "./dev-bwDgSObH.js";
2
2
  import { $ as r, Q as i, X as a, Z as o, s, t as c, y as l } from "./webDev-Dybxew86.js";
3
3
  import { w as u } from "./frameworkTableAssembly-oMPBKqlE.js";
4
4
  import { n as d } from "./pageConvention-cEiRxdab.js";
5
- import { r as f, u as p } from "./apiBuild-DiE9zcj6.js";
5
+ import { r as f, u as p } from "./apiBuild-DGsShRuF.js";
6
6
  import { n as m, r as h, t as g } from "./cliOutput-D1tSBoRM.js";
7
7
  import { n as ee, t as te } from "./tsconfigPaths-BWXBWgcl.js";
8
8
  import { n as ne, r as re } from "./fileTaxonomy-CJfgOllU.js";
@@ -1245,7 +1245,7 @@ var le = /* @__PURE__ */ new Set([
1245
1245
  let t = zt.get(e);
1246
1246
  if (t !== void 0) return t;
1247
1247
  let n = (async () => {
1248
- let { walk: t, loadDiscovered: n } = await import("./dev-BSFTgCQ5.js"), r = await t(e);
1248
+ let { walk: t, loadDiscovered: n } = await import("./dev-BV6XNYKf.js"), r = await t(e);
1249
1249
  return {
1250
1250
  files: r,
1251
1251
  discovered: await n(r)
@@ -1333,7 +1333,7 @@ var le = /* @__PURE__ */ new Set([
1333
1333
  }
1334
1334
  }, J = async (r) => {
1335
1335
  try {
1336
- let { loadApiConfig: i } = await import("./dev-BSFTgCQ5.js"), { discovered: a } = await U(r), o = n(await i(r)), s = {
1336
+ let { loadApiConfig: i } = await import("./dev-BV6XNYKf.js"), { discovered: a } = await U(r), o = n(await i(r)), s = {
1337
1337
  queries: a.queries,
1338
1338
  mutations: a.mutations,
1339
1339
  actions: a.actions,
@@ -1417,7 +1417,7 @@ var le = /* @__PURE__ */ new Set([
1417
1417
  }, Jt = async (e) => Xe({
1418
1418
  hasWorkflows: async () => (await U(e)).discovered.workflows.length > 0,
1419
1419
  openStore: async () => {
1420
- let { loadApiConfig: t, buildStore: n } = await import("./dev-BSFTgCQ5.js"), r = await t(e);
1420
+ let { loadApiConfig: t, buildStore: n } = await import("./dev-BV6XNYKf.js"), r = await t(e);
1421
1421
  if (r === null) throw Error("no app.config.ts in this directory");
1422
1422
  let i = await n(r, void 0);
1423
1423
  return {
@@ -1497,7 +1497,7 @@ var le = /* @__PURE__ */ new Set([
1497
1497
  }
1498
1498
  }, en = async (e) => {
1499
1499
  try {
1500
- let { loadApiConfig: t } = await import("./dev-BSFTgCQ5.js"), n = (await t(e))?.doctor?.scopeVocabulary;
1500
+ let { loadApiConfig: t } = await import("./dev-BV6XNYKf.js"), n = (await t(e))?.doctor?.scopeVocabulary;
1501
1501
  if (typeof n != "string") return [];
1502
1502
  let [r, i] = n.split("#");
1503
1503
  if (r === void 0 || i === void 0) return [];
@@ -1513,7 +1513,7 @@ var le = /* @__PURE__ */ new Set([
1513
1513
  findings: []
1514
1514
  };
1515
1515
  try {
1516
- let { offlineManifest: n, toInput: r } = await import("./checkCommand-BTA6P3Yr.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-zuMGKVYX.js"), o = await n(e), s = r(o.manifest, o.tables), c = await en(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
1516
+ let { offlineManifest: n, toInput: r } = await import("./checkCommand-CIPYyNyc.js"), { buildAppGraph: i, checkAppGraph: a } = await import("./appGraph-zuMGKVYX.js"), o = await n(e), s = r(o.manifest, o.tables), c = await en(e), l = [.../* @__PURE__ */ new Set([...s.scopes ?? [], ...c])];
1517
1517
  if (l.length === 0) return t;
1518
1518
  let u = i({
1519
1519
  ...s,
@@ -1555,7 +1555,7 @@ var le = /* @__PURE__ */ new Set([
1555
1555
  if (n === void 0 || r.length === 0) return;
1556
1556
  let i = [];
1557
1557
  try {
1558
- let { loadApiConfig: t } = await import("./dev-BSFTgCQ5.js");
1558
+ let { loadApiConfig: t } = await import("./dev-BV6XNYKf.js");
1559
1559
  i = (await t(e))?.locales ?? [];
1560
1560
  } catch {
1561
1561
  return;
@@ -1571,7 +1571,7 @@ var le = /* @__PURE__ */ new Set([
1571
1571
  }, rn = async (e) => {
1572
1572
  let { discovered: t } = await U(e), n = [];
1573
1573
  try {
1574
- let { loadApiConfig: t } = await import("./dev-BSFTgCQ5.js");
1574
+ let { loadApiConfig: t } = await import("./dev-BV6XNYKf.js");
1575
1575
  n = ((await t(e))?.plugins ?? []).map((e) => {
1576
1576
  let t = (e.extendSchema?.tables ?? []).map((e) => {
1577
1577
  let t = e;
@@ -0,0 +1,2 @@
1
+ import { v as e, y as t } from "./doctorCommand-BXyrtN2l.js";
2
+ export { e as runDoctorCommand, t as runServePreflight };
@@ -1,4 +1,4 @@
1
- import { c as e, i as t } from "./dev-Dc4cpmRl.js";
1
+ import { c as e, i as t } from "./dev-bwDgSObH.js";
2
2
  import { n, t as r } from "./cliArgs-D4p8n7EE.js";
3
3
  import { r as i } from "./appPort-B_HpJ_ck.js";
4
4
  import { resolve as a } from "node:path";
@@ -1,4 +1,4 @@
1
- import { c as e, i as t } from "./dev-Dc4cpmRl.js";
1
+ import { c as e, i as t } from "./dev-bwDgSObH.js";
2
2
  import { Effect as n } from "effect";
3
3
  import { embedMany as r } from "@voltro/ai";
4
4
  //#region src/embeddingsBackfill.ts
@@ -1,4 +1,4 @@
1
- import { c as e } from "./dev-Dc4cpmRl.js";
1
+ import { c as e } from "./dev-bwDgSObH.js";
2
2
  import { at as t, ct as n, lt as r, ot as i } from "./webDev-Dybxew86.js";
3
3
  import { join as a } from "node:path";
4
4
  import { promises as o } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  import { n as e } from "./cliOutput-D1tSBoRM.js";
2
- import { t } from "./checkCommand-BhG0gmUD.js";
3
- import { i as n, n as r } from "./codemodRunner-DjQGrdmg.js";
2
+ import { t } from "./checkCommand-CLPTzYqM.js";
3
+ import { i as n, n as r } from "./codemodRunner-cyqu5oxr.js";
4
4
  import { basename as i, join as a, resolve as o } from "node:path";
5
5
  import { branchNamespaceName as s, planBranchProvision as c } from "@voltro/database";
6
6
  import { readFile as l } from "node:fs/promises";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./commands-Bp7utV-7.js";
2
- import { r as o } from "./templates-De8IR5-c.js";
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./commands-BwG9A9Vv.js";
2
+ import { r as o } from "./templates-BR-fb4SP.js";
3
3
  //#region src/index.ts
4
4
  var s = "framework";
5
5
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { n as e, r as t, t as n } from "./cliOutput-D1tSBoRM.js";
2
- import { o as r } from "./updateCommand-CwnUK_mf.js";
2
+ import { o as r } from "./updateCommand-9GHHsmDf.js";
3
3
  import { join as i, resolve as a } from "node:path";
4
4
  import { readFileSync as o, readdirSync as s, statSync as c } from "node:fs";
5
5
  //#region src/infoCommand.ts
@@ -0,0 +1,4 @@
1
+ //#region src/inspectGateHint.ts
2
+ var e = (e) => e === 401 || e === 403, t = (e) => e === 403 ? "The inspect surface rejected the credential. VOLTRO_INSPECT_TOKEN must be set on the SERVER and in this shell, and the two values must match. (This is a framework gate, not a database permission.)" : "The inspect surface is gated and this request carried no credential. Set VOLTRO_INSPECT_TOKEN on the server and in this shell (same value), or pass --token.";
3
+ //#endregion
4
+ export { e as n, t };
@@ -1,7 +1,7 @@
1
1
  import { i as e, n as t, r as n } from "./cliArgs-D4p8n7EE.js";
2
2
  import { t as r } from "./cliRuntime-Dh7UDinH.js";
3
3
  import { n as i } from "./renderProfile-1OWWAAtx.js";
4
- import { a, i as o } from "./projectScaffold-LMMtaavR.js";
4
+ import { a, i as o } from "./projectScaffold-DDgWbNLe.js";
5
5
  import { t as s } from "./discover-C9XKJDco.js";
6
6
  import { basename as c, join as l, relative as u, resolve as d } from "node:path";
7
7
  import { createLogger as f } from "@voltro/logger";
@@ -76,7 +76,7 @@ var _ = /* @__PURE__ */ new Set([
76
76
  return t(n);
77
77
  }, T = async (e) => {
78
78
  if (e.includes("--create-only")) return s(S(u(e.find((e) => !e.startsWith("-")) ?? process.cwd())), { scope: "voltro:migrate" });
79
- let { runDbCommand: t } = await import("./dbCommand-DS4b97Is.js");
79
+ let { runDbCommand: t } = await import("./dbCommand-CC-qNzpz.js");
80
80
  return t(["apply", ...e]);
81
81
  };
82
82
  //#endregion
@@ -136,11 +136,12 @@ var _ = d({ scope: "voltro:privacy" }), v = /* @__PURE__ */ new Set([
136
136
  let s = a.data.entry;
137
137
  return o(s ?? {}), s?.truncated !== void 0 && s.truncated.length > 0 ? (_.error(`voltro privacy erase: INCOMPLETE — the row cap was hit on ${s.truncated.join(", ")}. Re-run until 'truncated' is absent.`), 1) : 0;
138
138
  }
139
- }, A = async (e) => {
139
+ }, A = (e) => Object.prototype.hasOwnProperty.call(k, e), j = async (e) => {
140
140
  let [t, ...n] = e;
141
141
  if (t === void 0) return console.error("voltro privacy <subcommand> [args]"), console.error(" scope [root] [--subject-table <t>] [--depth <n>] [--exclude a,b] [--json]"), console.error(" Derive the DSAR subject graph from the schema — and print what it CANNOT see."), console.error(" export <subjectId> --url <app> [--token <t>] [--out <file>]"), console.error(" erase <subjectId> --url <app> [--token <t>] [--mode delete|anonymize] --confirm"), console.error(" Both run against the RUNNING app's admin-gated governance endpoint, so the"), console.error(" configured anonymize fields and the erasure log apply."), 1;
142
+ if (!A(t)) return console.error(`voltro privacy: unknown subcommand '${t}'`), 2;
142
143
  let r = k[t];
143
- return r === void 0 ? (console.error(`voltro privacy: unknown subcommand '${t}'`), 2) : r(n);
144
+ return r(n);
144
145
  };
145
146
  //#endregion
146
- export { A as runPrivacyCommand };
147
+ export { j as runPrivacyCommand };
@@ -1,11 +1,12 @@
1
1
  import { n as e } from "./cliOutput-D1tSBoRM.js";
2
2
  import { n as t, o as n, r, t as i } from "./inspectFetch-EMuhTG_9.js";
3
- import { ansi as a } from "@voltro/logger";
3
+ import { n as a, t as o } from "./inspectGateHint-BF6608UT.js";
4
+ import { ansi as s } from "@voltro/logger";
4
5
  //#region src/probeCommand.ts
5
- var o = () => {
6
- console.log(`${a.bold("voltro probe")} — ask a RUNNING app whether its declared access is enforced
6
+ var c = () => {
7
+ console.log(`${s.bold("voltro probe")} — ask a RUNNING app whether its declared access is enforced
7
8
 
8
- ${a.bold("access")} call every guarded procedure with NO credentials and
9
+ ${s.bold("access")} call every guarded procedure with NO credentials and
9
10
  report any that answer anyway
10
11
 
11
12
  Options
@@ -19,9 +20,9 @@ it probes ANONYMOUSLY, so it cannot tell one scope from another. It answers
19
20
  exactly one question — does a procedure that DECLARES a guard admit a caller
20
21
  that presents nothing? A guard declared and never bound is the failure it finds.
21
22
 
22
- The target must expose the inspect surface: set ${a.bold("VOLTRO_INSPECT_TOKEN")} on the
23
+ The target must expose the inspect surface: set ${s.bold("VOLTRO_INSPECT_TOKEN")} on the
23
24
  server and in this shell.`);
24
- }, s = (e) => e.filter((e) => (e.guards ?? []).some((e) => e.kind !== "open")), c = (e) => {
25
+ }, l = (e) => e.filter((e) => (e.guards ?? []).some((e) => e.kind !== "open")), u = (e) => {
25
26
  if (e.kind === "err") return {
26
27
  verdict: "inconclusive",
27
28
  detail: "transport failure — the app did not answer"
@@ -56,16 +57,16 @@ server and in this shell.`);
56
57
  verdict: "refused",
57
58
  detail: `refused (${a})`
58
59
  };
59
- }, l = 0, u = (e) => ({
60
+ }, d = 0, f = (e) => ({
60
61
  _tag: "Request",
61
- id: String(++l),
62
+ id: String(++d),
62
63
  tag: e,
63
64
  payload: {},
64
65
  headers: []
65
- }), d = async (e, t) => {
66
- let { verdict: n, detail: r } = c(await i(e, "/rpc", {
66
+ }), p = async (e, t) => {
67
+ let { verdict: n, detail: r } = u(await i(e, "/rpc", {
67
68
  method: "POST",
68
- body: u(t.tag),
69
+ body: f(t.tag),
69
70
  anonymous: !0
70
71
  }));
71
72
  return {
@@ -74,49 +75,49 @@ server and in this shell.`);
74
75
  verdict: n,
75
76
  detail: r
76
77
  };
77
- }, f = (t, n, r, i) => {
78
- let o = n.filter((e) => e.verdict === "admitted"), s = n.filter((e) => e.verdict === "inconclusive"), c = n.filter((e) => e.verdict === "refused");
78
+ }, m = (t, n, r, i) => {
79
+ let a = n.filter((e) => e.verdict === "admitted"), o = n.filter((e) => e.verdict === "inconclusive"), c = n.filter((e) => e.verdict === "refused");
79
80
  if (r === "json") e({
80
81
  app: t,
81
82
  probed: n.length,
82
83
  refused: c.length,
83
- admitted: o.length,
84
- inconclusive: s.length,
84
+ admitted: a.length,
85
+ inconclusive: o.length,
85
86
  results: n
86
87
  });
87
- else if (n.length === 0) console.log(`${a.yellow("○")} ${t}: no guarded procedure to probe — every route is openAccess or undecided.`), console.log(" Undecided routes are `voltro check`'s finding, not this one's.");
88
+ else if (n.length === 0) console.log(`${s.yellow("○")} ${t}: no guarded procedure to probe — every route is openAccess or undecided.`), console.log(" Undecided routes are `voltro check`'s finding, not this one's.");
88
89
  else {
89
- console.log(`${a.bold(t)}: probed ${n.length} guarded procedure(s) with NO credentials`);
90
- for (let e of o) console.log(` ${a.red("✗")} ${a.bold(e.tag)} — ${e.detail}`);
91
- for (let e of s) console.log(` ${a.yellow("?")} ${e.tag} — ${e.detail}`);
92
- console.log(` ${a.green("✓")} ${c.length} refused · ${a.yellow(String(s.length))} inconclusive · ${a.red(String(o.length))} admitted`);
90
+ console.log(`${s.bold(t)}: probed ${n.length} guarded procedure(s) with NO credentials`);
91
+ for (let e of a) console.log(` ${s.red("✗")} ${s.bold(e.tag)} — ${e.detail}`);
92
+ for (let e of o) console.log(` ${s.yellow("?")} ${e.tag} — ${e.detail}`);
93
+ console.log(` ${s.green("✓")} ${c.length} refused · ${s.yellow(String(o.length))} inconclusive · ${s.red(String(a.length))} admitted`);
93
94
  }
94
- return o.length > 0 ? (r !== "json" && console.error(`\n${a.red("A declared guard did not refuse an anonymous caller.")}\n The declaration and the behaviour disagree — the guard is on the descriptor and
95
+ return a.length > 0 ? (r !== "json" && console.error(`\n${s.red("A declared guard did not refuse an anonymous caller.")}\n The declaration and the behaviour disagree — the guard is on the descriptor and
95
96
  nothing enforces it on this path. Check that the procedure is bound through the
96
- dispatch spine (not a hand-mounted route), and that its guard list is not empty.`), 1) : i && s.length > 0 ? (r !== "json" && console.error(`\n${a.yellow("--strict:")} ${s.length} procedure(s) could not be judged.\n Their calls failed before the guard was reached (usually payload validation).
97
+ dispatch spine (not a hand-mounted route), and that its guard list is not empty.`), 1) : i && o.length > 0 ? (r !== "json" && console.error(`\n${s.yellow("--strict:")} ${o.length} procedure(s) could not be judged.\n Their calls failed before the guard was reached (usually payload validation).
97
98
  That is not evidence of enforcement, so under --strict it is not a pass.`), 1) : 0;
98
- }, p = async (e) => {
99
- let o = t(e);
100
- if ("error" in o) return console.error(`voltro probe access: ${o.error}`), 1;
101
- let { format: c } = o, { process: l, target: u, rest: p, error: m } = r(o.rest);
102
- if (m !== void 0) return console.error(`voltro probe access: ${m}`), 1;
103
- let h = p.includes("--strict"), g = await n(l, u);
104
- if (g.length === 0) return console.error("voltro probe access: no live api found.\n This command asks a RUNNING app what it does — there is nothing to ask a source tree.\n Start one (`voltro dev` / `voltro serve`), or address it directly with --url."), 1;
105
- let _ = 0;
106
- for (let e of g) {
99
+ }, h = async (e) => {
100
+ let c = t(e);
101
+ if ("error" in c) return console.error(`voltro probe access: ${c.error}`), 1;
102
+ let { format: u } = c, { process: d, target: f, rest: h, error: g } = r(c.rest);
103
+ if (g !== void 0) return console.error(`voltro probe access: ${g}`), 1;
104
+ let _ = h.includes("--strict"), v = await n(d, f);
105
+ if (v.length === 0) return console.error("voltro probe access: no live api found.\n This command asks a RUNNING app what it does — there is nothing to ask a source tree.\n Start one (`voltro dev` / `voltro serve`), or address it directly with --url."), 1;
106
+ let y = 0;
107
+ for (let e of v) {
107
108
  let t = await i(e, "/_voltro/inspect/manifest");
108
109
  if (t.kind === "err" || t.status < 200 || t.status >= 300) {
109
- console.error(`${a.red("✗")} ${e.name}: could not read the manifest — ${t.kind === "err" ? t.error : `HTTP ${t.status}`}`), t.kind === "ok" && t.status === 401 && console.error(" Set VOLTRO_INSPECT_TOKEN on the server and in this shell."), _ = 1;
110
+ console.error(`${s.red("✗")} ${e.name}: could not read the manifest — ${t.kind === "err" ? t.error : `HTTP ${t.status}`}`), t.kind === "ok" && a(t.status) && console.error(` ${o(t.status)}`), y = 1;
110
111
  continue;
111
112
  }
112
- let n = s(t.data?.procedures ?? []), r = [];
113
- for (let t of n) r.push(await d(e, t));
114
- _ = Math.max(_, f(e.name, r, c, h));
113
+ let n = l(t.data?.procedures ?? []), r = [];
114
+ for (let t of n) r.push(await p(e, t));
115
+ y = Math.max(y, m(e.name, r, u, _));
115
116
  }
116
- return _;
117
- }, m = async (e) => {
117
+ return y;
118
+ }, g = async (e) => {
118
119
  let [t, ...n] = e;
119
- return t === void 0 || t === "--help" || t === "-h" ? (o(), +(t === void 0)) : t === "access" ? p(n) : (console.error(`voltro probe: unknown subcommand "${t}" (expected: access)`), 1);
120
+ return t === void 0 || t === "--help" || t === "-h" ? (c(), +(t === void 0)) : t === "access" ? h(n) : (console.error(`voltro probe: unknown subcommand "${t}" (expected: access)`), 1);
120
121
  };
121
122
  //#endregion
122
- export { m as runProbeCommand };
123
+ export { g as runProbeCommand };
@@ -1,2 +1,2 @@
1
- import { c as e, l as t, s as n } from "./projectScaffold-LMMtaavR.js";
1
+ import { c as e, l as t, s as n } from "./projectScaffold-DDgWbNLe.js";
2
2
  export { n as runAddApp, e as runCreateProject, t as runListTemplates };
@@ -1,9 +1,9 @@
1
- import { a as e, i as t, n, r, t as i } from "./templates-De8IR5-c.js";
1
+ import { a as e, i as t, n, r, t as i } from "./templates-BR-fb4SP.js";
2
2
  import { i as a, n as o, t as s } from "./cloudClient-DWL-Hw_T.js";
3
3
  import { t as c } from "./cliRuntime-Dh7UDinH.js";
4
4
  import { n as l } from "./agentsMd-mhQMF1bx.js";
5
5
  import { n as u } from "./addCommand-BNeoeSxe.js";
6
- import { a as d, n as f, r as p } from "./baselineCommand-DIttzO8A.js";
6
+ import { a as d, n as f, r as p } from "./baselineCommand-C6NMt-oa.js";
7
7
  import { basename as m, dirname as h, join as g, resolve as _ } from "node:path";
8
8
  import { existsSync as v, mkdirSync as y, promises as b, writeFileSync as x } from "node:fs";
9
9
  import { createLogger as S } from "@voltro/logger";
@@ -1,4 +1,4 @@
1
- import { n as e } from "./apiBuild-DiE9zcj6.js";
1
+ import { n as e } from "./apiBuild-DGsShRuF.js";
2
2
  import { join as t, resolve as n, sep as r } from "node:path";
3
3
  import { existsSync as i, promises as a } from "node:fs";
4
4
  import { createLogger as o } from "@voltro/logger";
@@ -422,7 +422,7 @@ let package = Package(
422
422
  await l.mkdir(o(e), { recursive: !0 }), await l.writeFile(e, n.content, "utf8");
423
423
  }
424
424
  }, X = async (e) => {
425
- let { offlineManifest: t } = await import("./checkCommand-BTA6P3Yr.js"), { manifest: n } = await t(e);
425
+ let { offlineManifest: t } = await import("./checkCommand-CIPYyNyc.js"), { manifest: n } = await t(e);
426
426
  return n;
427
427
  }, Z = async (e) => {
428
428
  let t = J(await (e.manifestProvider ?? X)(e.root), {
@@ -2,7 +2,7 @@ import { t as e } from "./nodeEnvironment-cGFAj1J8.js";
2
2
  import { t } from "./frameworkInspectState-CX2250XB.js";
3
3
  import { r as n, t as r } from "./appModuleLoader-C9r9mxZt.js";
4
4
  import { n as i, t as a } from "./startupRunner-DPGFchOa.js";
5
- import { $ as o, $t as s, A as c, At as l, B as u, C as d, Ct as ee, D as f, Dt as p, E as te, Et as ne, F as re, Ft as m, G as ie, Gt as ae, H as oe, Ht as se, I as h, It as ce, J as le, Jt as g, K as ue, Kt as de, L as fe, Lt as pe, M as me, Mt as he, N as ge, Nt as _e, O as _, Ot as ve, P as ye, Pt as be, Q as xe, Qt as Se, R as Ce, S as we, St as Te, T as v, Tt as Ee, U as De, Ut as Oe, V as ke, Vt as Ae, W as je, Wt as y, X as Me, Xt as Ne, Y as Pe, Yt as Fe, Z as b, Zt as Ie, _ as Le, _n as x, _t as Re, an as ze, at as Be, b as Ve, bt as He, c as Ue, cn as We, ct as Ge, dn as S, dt as Ke, en as qe, et as Je, f as Ye, fn as Xe, ft as Ze, g as Qe, gn as $e, gt as et, h as tt, hn as nt, ht as rt, i as C, in as it, it as at, j as ot, jt as st, k as ct, kt as lt, ln as ut, lt as dt, m as ft, mn as pt, mt, nn as ht, nt as gt, o as w, on as _t, ot as T, p as vt, pt as yt, q as bt, qt as E, r as D, rn as xt, rt as St, sn as Ct, st as wt, t as Tt, tn as O, tt as Et, u as Dt, un as Ot, ut as kt, v as At, vn as jt, vt as Mt, w as Nt, wt as Pt, x as Ft, xt as It, y as k, yt as Lt, zt as A } from "./dev-Dc4cpmRl.js";
5
+ import { $ as o, $t as s, A as c, At as l, B as u, C as d, Ct as ee, D as f, Dt as p, E as te, Et as ne, F as re, Ft as m, G as ie, Gt as ae, H as oe, Ht as se, I as h, It as ce, J as le, Jt as g, K as ue, Kt as de, L as fe, Lt as pe, M as me, Mt as he, N as ge, Nt as _e, O as _, Ot as ve, P as ye, Pt as be, Q as xe, Qt as Se, R as Ce, S as we, St as Te, T as v, Tt as Ee, U as De, Ut as Oe, V as ke, Vt as Ae, W as je, Wt as y, X as Me, Xt as Ne, Y as Pe, Yt as Fe, Z as b, Zt as Ie, _ as Le, _n as x, _t as Re, an as ze, at as Be, b as Ve, bt as He, c as Ue, cn as We, ct as Ge, dn as S, dt as Ke, en as qe, et as Je, f as Ye, fn as Xe, ft as Ze, g as Qe, gn as $e, gt as et, h as tt, hn as nt, ht as rt, i as C, in as it, it as at, j as ot, jt as st, k as ct, kt as lt, ln as ut, lt as dt, m as ft, mn as pt, mt, nn as ht, nt as gt, o as w, on as _t, ot as T, p as vt, pt as yt, q as bt, qt as E, r as D, rn as xt, rt as St, sn as Ct, st as wt, t as Tt, tn as O, tt as Et, u as Dt, un as Ot, ut as kt, v as At, vn as jt, vt as Mt, w as Nt, wt as Pt, x as Ft, xt as It, y as k, yt as Lt, zt as A } from "./dev-bwDgSObH.js";
6
6
  import { n as Rt } from "./cliArgs-D4p8n7EE.js";
7
7
  import { r as zt } from "./appPort-B_HpJ_ck.js";
8
8
  import { at as Bt, nt as Vt, st as Ht } from "./webDev-Dybxew86.js";
@@ -15,7 +15,7 @@ import { h as ln, o as un } from "./inspect-CjTYzAs_.js";
15
15
  import { n as dn } from "./mssqlClusterPatch-_4cE_nun.js";
16
16
  import { i as fn, n as pn, r as mn, t as hn } from "./webhookDiscovery-D7VaeMlz.js";
17
17
  import { t as gn } from "./inspectMetrics-CfdKLh6t.js";
18
- import { r as _n, u as vn } from "./apiBuild-DiE9zcj6.js";
18
+ import { r as _n, u as vn } from "./apiBuild-DGsShRuF.js";
19
19
  import { t as yn } from "./bootTiming-BdyP9nYw.js";
20
20
  import { t as bn } from "./dataProfile-dW-PsfLB.js";
21
21
  import { basename as xn, join as Sn } from "node:path";
@@ -0,0 +1,2 @@
1
+ import { n as e } from "./serveCommand-CyZsXL1l.js";
2
+ export { e as runServeDispatch };
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./loadEnv-D9nEOClM.js";
2
2
  import { r as t } from "./appModuleLoader-C9r9mxZt.js";
3
3
  import { i as n } from "./dialectDriver-CgXnDfec.js";
4
- import { t as r } from "./serveCommand-VVGcIMIw.js";
4
+ import { t as r } from "./serveCommand-CyZsXL1l.js";
5
5
  export { e as loadDotEnv, t as registerAppModules, n as registerDriver, r as runServe };
@@ -0,0 +1,27 @@
1
+ //#region src/subcommandNames.ts
2
+ var e = [
3
+ "plan",
4
+ "apply",
5
+ "branch",
6
+ "plans",
7
+ "drift",
8
+ "squash",
9
+ "restore-snapshot",
10
+ "generate",
11
+ "migrate",
12
+ "rollback",
13
+ "status",
14
+ "files",
15
+ "rollback-file",
16
+ "seed",
17
+ "gc-snapshots",
18
+ "adopt",
19
+ "scan-credentials",
20
+ "encrypt-column"
21
+ ], t = [
22
+ "scope",
23
+ "export",
24
+ "erase"
25
+ ], n = (e) => `<${e.join("|")}>`;
26
+ //#endregion
27
+ export { t as n, n as r, e as t };
@@ -4,10 +4,7 @@ import { fileURLToPath as s } from "node:url";
4
4
  //#region src/templates.ts
5
5
  var c = e(s(import.meta.url)), l = (() => {
6
6
  let n = process.env.VOLTRO_TEMPLATES_DIR;
7
- if (n) {
8
- let e = t(n, "apps");
9
- if (i(e)) return e;
10
- }
7
+ if (n) return t(n, "apps");
11
8
  let a = r(c, "..", "templates", "apps"), o = c;
12
9
  for (let n = 0; n < 12; n++) {
13
10
  for (let e of ["voltro-templates", ".voltro-templates"]) {
@@ -79,7 +79,7 @@ var d = l({ scope: "voltro:test" }), f = (e) => {
79
79
  }, C = async (e) => {
80
80
  let t = m(e);
81
81
  if (!o(t.root)) return console.error(`voltro test: no such directory: ${t.root}`), 1;
82
- let { checkRpcGroupFreshness: n } = await import("./codegen-ChBi_hVa.js"), r = await n(t.root);
82
+ let { checkRpcGroupFreshness: n } = await import("./codegen-uYrrDzQv.js"), r = await n(t.root);
83
83
  if (r.kind === "stale") return console.error(`voltro test: ${r.file} is stale — ${r.reason}.`), console.error(" The tests would run against the previously generated procedure group, pass, and prove nothing about the descriptors you just edited.\n Run `voltro codegen` (or boot `voltro dev` once) and try again."), 1;
84
84
  d.info("running vitest", {
85
85
  root: t.root,
@@ -1,4 +1,4 @@
1
- import { n as e, r as t, t as n } from "./codemodRunner-DjQGrdmg.js";
1
+ import { n as e, r as t, t as n } from "./codemodRunner-cyqu5oxr.js";
2
2
  import { basename as r, dirname as i, join as a, relative as o, resolve as s } from "node:path";
3
3
  import { existsSync as c, readFileSync as l, readdirSync as u, statSync as d, writeFileSync as f } from "node:fs";
4
4
  import { totalmem as p } from "node:os";
@@ -0,0 +1,2 @@
1
+ import { f as e, p as t } from "./updateCommand-9GHHsmDf.js";
2
+ export { e as runApplyCodemods, t as runUpdateCommand };
@@ -222,7 +222,7 @@ createVerifier({ secret: [process.env.WEBHOOK_SECRET, process.env.WEBHOOK_SECRET
222
222
  ...t === void 0 ? {} : { payload: t }
223
223
  };
224
224
  }, S = u({ scope: "voltro:webhooks" }), C = ["--out", "--name"], w = async (e) => {
225
- let { walk: t, loadDiscovered: n } = await import("./dev-BSFTgCQ5.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-CrGAfhIG.js");
225
+ let { walk: t, loadDiscovered: n } = await import("./dev-BV6XNYKf.js"), { outgoingFromEvents: r } = await import("./webhookDiscovery-CrGAfhIG.js");
226
226
  return r((await n(await t(e))).events.map((e) => ({
227
227
  file: e.file,
228
228
  descriptor: e.descriptor
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/cli",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "description": "The `voltro` CLI — dev server, codegen, migrations, project scaffolding, agent-docs seeding, and production serve.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -574,6 +574,12 @@
574
574
  "title": "A 401 is `code: 'unauthorized'`; the vault's own failure is `'credential_unusable'`",
575
575
  "kind": "transform"
576
576
  },
577
+ {
578
+ "version": "0.37.0",
579
+ "id": "0.37.0/01_procedure-input-rejects-undeclared-fields",
580
+ "title": "a procedure input rejects a field it does not declare (it used to drop it)",
581
+ "kind": "manual"
582
+ },
577
583
  {
578
584
  "version": "0.4.0",
579
585
  "id": "0.4.0/01_rbac-forbidden-to-scopeerror",
@@ -673,22 +679,22 @@
673
679
  "@effect/platform-node": "^0.108.0",
674
680
  "@effect/sql": "^0.52.0",
675
681
  "@effect/workflow": "^0.19.0",
676
- "@voltro/ai": "0.36.0",
677
- "@voltro/cache": "0.36.0",
678
- "@voltro/data-transfer": "0.36.0",
679
- "@voltro/database": "0.36.0",
680
- "@voltro/env": "0.36.0",
681
- "@voltro/kv": "0.36.0",
682
- "@voltro/logger": "0.36.0",
683
- "@voltro/plugin-auth": "0.36.0",
684
- "@voltro/plugin-broadcast": "0.36.0",
685
- "@voltro/plugin-mail": "0.36.0",
686
- "@voltro/plugin-storage": "0.36.0",
687
- "@voltro/plugin-webhooks": "0.36.0",
688
- "@voltro/protocol": "0.36.0",
689
- "@voltro/runtime": "0.36.0",
690
- "@voltro/serverless": "0.36.0",
691
- "@voltro/workflow": "0.36.0",
682
+ "@voltro/ai": "0.37.0",
683
+ "@voltro/cache": "0.37.0",
684
+ "@voltro/data-transfer": "0.37.0",
685
+ "@voltro/database": "0.37.0",
686
+ "@voltro/env": "0.37.0",
687
+ "@voltro/kv": "0.37.0",
688
+ "@voltro/logger": "0.37.0",
689
+ "@voltro/plugin-auth": "0.37.0",
690
+ "@voltro/plugin-broadcast": "0.37.0",
691
+ "@voltro/plugin-mail": "0.37.0",
692
+ "@voltro/plugin-storage": "0.37.0",
693
+ "@voltro/plugin-webhooks": "0.37.0",
694
+ "@voltro/protocol": "0.37.0",
695
+ "@voltro/runtime": "0.37.0",
696
+ "@voltro/serverless": "0.37.0",
697
+ "@voltro/workflow": "0.37.0",
692
698
  "chokidar": "^5.0.0",
693
699
  "ioredis": "^5.11.1",
694
700
  "tinyglobby": "^0.2.17",
@@ -707,7 +707,7 @@ each plugin's own README.
707
707
 
708
708
  | Topic | Open | Summary |
709
709
  |---|---|---|
710
- | **What's new in 0.36.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
710
+ | **What's new in 0.37.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
711
711
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
712
712
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
713
713
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -9,7 +9,7 @@ each plugin's own README.
9
9
 
10
10
  | Topic | Open | Summary |
11
11
  |---|---|---|
12
- | **What's new in 0.36.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
12
+ | **What's new in 0.37.0** | `node_modules/@voltro/cli/templates/agent-docs/whats-new.md` | Everything that changed in this version. Read it before hand-rolling something the framework may now ship. |
13
13
  | AI | `node_modules/@voltro/cli/templates/agent-docs/ai.md` | How Voltro treats AI — agents, tools, streaming, RAG — all primitives over the same WebSocket as the rest of the framework. |
14
14
  | Authentication | `node_modules/@voltro/cli/templates/agent-docs/authentication.md` | How @voltro/plugin-auth wires password + session-cookie auth across api + web, plus the pluggable identity-strategy protocol. |
15
15
  | Caching | `node_modules/@voltro/cli/templates/agent-docs/caching.md` | Voltro's caching layer (@voltro/cache) — an always-on memory default, swappable Redis-compatible backends, a low-level wrap primitive, and automatic query-result invalidation. |
@@ -173,6 +173,22 @@ voltro db apply # execute it (dev)
173
173
  # `voltro migrate` is an alias of `db apply` — same differ, shorter name (CI / ops)
174
174
  ```
175
175
 
176
+ ### "I want to encrypt a column that already has rows in it"
177
+
178
+ Turning on `.encrypted()` needs two things: the cipher, and a migration of the
179
+ rows that are already there. The second has a command — it is easy to miss,
180
+ because nothing about a schema change suggests a data pass is owed.
181
+
182
+ ```bash
183
+ voltro db scan-credentials # find plaintext secrets, column by column
184
+ voltro db encrypt-column users.apiToken --dry-run
185
+ voltro db encrypt-column users.apiToken --yes
186
+ ```
187
+
188
+ `--key-env` names the key variable if it is not the default; the key must be the
189
+ SAME one the app runs with, or the rows come back undecryptable. `voltro db`
190
+ with no subcommand prints the full list.
191
+
176
192
  ### "I want a clean rebuild"
177
193
 
178
194
  ```bash
@@ -263,6 +263,36 @@ const { data } = useSubscription('app', 'messages.list', {
263
263
  })
264
264
  ```
265
265
 
266
+ ### An undeclared field is refused, not dropped
267
+
268
+ The input schema is the whole accepted key set. A field it does not declare
269
+ fails the call with a `ParseError` naming the key and listing what was expected:
270
+
271
+ ```
272
+ { readonly channelId: string; readonly limit: number }
273
+ └─ ["employeeId"]
274
+ └─ is unexpected, expected: "channelId" | "limit"
275
+ ```
276
+
277
+ It used to be discarded silently, and the reason that is worse than it sounds is
278
+ what a discarded FILTER means. A consumer's layout called a `userSettings.list`
279
+ that declares `userId` with `{ employeeId }`; the payload decoded to `{}`, which
280
+ for a list query is not a narrower filter but the absence of one, and an admin
281
+ was served another user's row.
282
+
283
+ The decoder cannot tell a projection field from a filter field, so it refuses
284
+ either way. Three consequences worth knowing:
285
+
286
+ - Nested objects and union members follow the same rule — a stray key inside
287
+ `{ page: { limit, offset } }` is refused too.
288
+ - `Schema.Struct({})` means "this procedure takes nothing", and a call carrying
289
+ anything is refused. `Schema.Record(...)` keeps its open key set: there the
290
+ openness is declared.
291
+ - If a call site legitimately holds more than the procedure declares — a spread
292
+ of a wider filter object — narrow it at the call site rather than widening the
293
+ schema. Widening restores the silent drop under a different name: the field is
294
+ accepted and still does nothing.
295
+
266
296
  ## Descriptor-return vs computed-return
267
297
 
268
298
  A query executor can return either: