@voltro/cli 0.33.0 → 0.35.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 (343) hide show
  1. package/CHANGELOG.md +1968 -0
  2. package/bin/nodeEnvironment.d.mts +30 -0
  3. package/bin/nodeEnvironment.mjs +158 -0
  4. package/bin/voltro.mjs +69 -5
  5. package/dist/addCommand-BNeoeSxe.js +124 -0
  6. package/dist/addCommand-aXSQveak.js +2 -0
  7. package/dist/agentsMd-BTchIZku.js +2 -0
  8. package/dist/agentsMd-mhQMF1bx.js +254 -0
  9. package/dist/apiBuild-B8aoJvuw.js +2 -0
  10. package/dist/{apiBuild-h9VHtnlw.js → apiBuild-DYD_ONLD.js} +46 -46
  11. package/dist/{appGraph-gQ_6GkQQ.js → appGraph-KGDPTuTy.js} +1 -1
  12. package/dist/appGraph-zuMGKVYX.js +2 -0
  13. package/dist/appPort-B_HpJ_ck.js +48 -0
  14. package/dist/baselineCommand-C2ClWZN3.js +2 -0
  15. package/dist/baselineCommand-DIttzO8A.js +227 -0
  16. package/dist/bin.js +71 -28
  17. package/dist/build-CD8K4XOr.js +711 -0
  18. package/dist/cacheCommand-DA4OH9xt.js +42 -0
  19. package/dist/capabilitiesCommand-nq_pz5xd.js +123 -0
  20. package/dist/checkCommand-Ct9xkTrS.js +232 -0
  21. package/dist/checkCommand-DKpDLlqu.js +2 -0
  22. package/dist/{cliArgs-qdZSElM3.js → cliArgs-D4p8n7EE.js} +12 -1
  23. package/dist/cliError-BmdYnghb.js +10 -0
  24. package/dist/cliOutput-D1tSBoRM.js +15 -0
  25. package/dist/{cliRuntime-Oh517vCV.js → cliRuntime-Dh7UDinH.js} +20 -20
  26. package/dist/cloudClient-DWL-Hw_T.js +67 -0
  27. package/dist/cloudCmd-Cvv5HGaZ.js +364 -0
  28. package/dist/clusterCmd-D5wsCmA_.js +54 -0
  29. package/dist/codegen-CYM3Zqrf.js +605 -0
  30. package/dist/codegen-ChBi_hVa.js +2 -0
  31. package/dist/codegenCommand-C4YoQIc2.js +30 -0
  32. package/dist/codemodRunner-BnFq3Fgu.js +5384 -0
  33. package/dist/commandRunner-BLAEFLjp.js +47 -0
  34. package/dist/commands-BE8E7zF3.js +816 -0
  35. package/dist/connectionConfig-UFlIEiys.js +66 -0
  36. package/dist/dashboardCommand-D7SgZGaN.js +25 -0
  37. package/dist/dataCommand-BhYwDgg-.js +537 -0
  38. package/dist/dataProfile-dW-PsfLB.js +15 -0
  39. package/dist/dbCommand-DS4b97Is.js +2 -0
  40. package/dist/{dbCommand-DTLKAfbA.js → dbCommand-O8HA63s2.js} +552 -402
  41. package/dist/{dev-C_P8FLSx.js → dev-C7sFZq3m.js} +3670 -3169
  42. package/dist/dev-D2BikO7a.js +3 -0
  43. package/dist/devActivity-Dx_3nnGv.js +100 -0
  44. package/dist/devActivity.js +1 -1
  45. package/dist/dialectDriver-CgXnDfec.js +39 -0
  46. package/dist/discover-C9XKJDco.js +25 -0
  47. package/dist/doctorCommand-CM4Ch9C7.js +2 -0
  48. package/dist/{checkCommand-xGhRFFg2.js → doctorCommand-DnimF5IM.js} +523 -1278
  49. package/dist/dormancyCommand-QewYug_s.js +69 -0
  50. package/dist/e2eCmd-BRabZww-.js +147 -0
  51. package/dist/embeddingsCommand-BfiLS_QI.js +73 -0
  52. package/dist/envCommand-CCGPRQY1.js +60 -0
  53. package/dist/evalCommand-6RUfPen4.js +118 -0
  54. package/dist/evolveCommand-CHsLCtDf.js +281 -0
  55. package/dist/fileTaxonomy-CJfgOllU.js +457 -0
  56. package/dist/frameworkTableAssembly-YyVe32Cb.js +2 -0
  57. package/dist/frameworkTableAssembly-oMPBKqlE.js +511 -0
  58. package/dist/generateCommand-DbgcUpGw.js +147 -0
  59. package/dist/index.d.ts +45 -0
  60. package/dist/index.js +4 -3
  61. package/dist/infoCommand-DwOgK1t6.js +60 -0
  62. package/dist/{inspect-BUUjt773.js → inspect-CjTYzAs_.js} +113 -41
  63. package/dist/inspect-P4pxoMaV.js +2 -0
  64. package/dist/inspectCmd-EHFZ9yYu.js +224 -0
  65. package/dist/inspectFetch-EMuhTG_9.js +151 -0
  66. package/dist/inspectMetrics-CfdKLh6t.js +72 -0
  67. package/dist/loadEnv-D9nEOClM.js +44 -0
  68. package/dist/logFileSink-C_D2wRN1.js +105 -0
  69. package/dist/logsCmd-D36xK7Zu.js +260 -0
  70. package/dist/manifestBuild-COkJoyAr.js +2 -0
  71. package/dist/{manifestBuild-BLrVuSlM.js → manifestBuild-hpPLaGxV.js} +1 -1
  72. package/dist/metaCommands-7MJfZ5cf.js +196 -0
  73. package/dist/migrate-BV7I-ZHZ.js +83 -0
  74. package/dist/mssqlClusterPatch-_4cE_nun.js +44 -0
  75. package/dist/newCommand-COWOJ1_E.js +156 -0
  76. package/dist/nodeEnvironment-cGFAj1J8.js +28 -0
  77. package/dist/packageCommand-Cug_3Ogl.js +271 -0
  78. package/dist/pageConvention-cEiRxdab.js +5 -0
  79. package/dist/privacyCommand-C-Df56U_.js +146 -0
  80. package/dist/probeCommand-CZfaaUOZ.js +122 -0
  81. package/dist/projectScaffold-DmzEKHib.js +2 -0
  82. package/dist/projectScaffold-LMMtaavR.js +814 -0
  83. package/dist/renderModeScan-D7J1B7Kw.js +105 -0
  84. package/dist/renderProfile-1OWWAAtx.js +81 -0
  85. package/dist/runtimeRegistry-DMeKfTHP.js +81 -0
  86. package/dist/runtimeTrace-CH3eUiMw.js +91 -0
  87. package/dist/scheduleCmd-DQRu6BZC.js +149 -0
  88. package/dist/scheduleManifestCmd-D2x0CTTY.js +249 -0
  89. package/dist/schemaIr-UJybUUZW.js +103 -0
  90. package/dist/{sdkgen-C81QIkiL.js → sdkgen-BLkvGRfX.js} +111 -209
  91. package/dist/seedRunner-ZmLSqNe2.js +333 -0
  92. package/dist/serveCommand-CbDHU6l-.js +2 -0
  93. package/dist/serveCommand-iwlUBNS1.js +1766 -0
  94. package/dist/serveEntry.js +5 -5
  95. package/dist/serverlessCommand-CfJZy6dS.js +482 -0
  96. package/dist/start-BgN62boB.js +3 -0
  97. package/dist/start-T4VesWiM.js +1087 -0
  98. package/dist/startEntry.js +2 -2
  99. package/dist/staticCommand-Dr2M6tpU.js +304 -0
  100. package/dist/storageCommand-Co6NfLqN.js +42 -0
  101. package/dist/templates-De8IR5-c.js +102 -0
  102. package/dist/test-CI6iDsYc.js +115 -0
  103. package/dist/tracesCmd-DStmCJPi.js +232 -0
  104. package/dist/tsconfigPaths-BWXBWgcl.js +107 -0
  105. package/dist/tsxLoader-EuXmSJ1K.js +51 -0
  106. package/dist/typecheckCommand-BlsWiCNq.js +61 -0
  107. package/dist/updateCommand-BlMZhWgO.js +2 -0
  108. package/dist/updateCommand-x0pI_x-B.js +585 -0
  109. package/dist/webDev-BcykISYQ2.js +2 -0
  110. package/dist/{inspectMetrics-1xzTKAFx.js → webDev-Dybxew86.js} +988 -1560
  111. package/dist/webhookDiscovery-CrGAfhIG.js +2 -0
  112. package/dist/webhookDiscovery-D7VaeMlz.js +51 -0
  113. package/dist/webhooksCommand-DlAgS2Iw.js +267 -0
  114. package/dist/workflowsCmd-BGF-mRZ5.js +608 -0
  115. package/package.json +209 -17
  116. package/templates/AGENTS.core.md +58 -3
  117. package/templates/AGENTS.md +64 -7
  118. package/templates/agent-docs/_index.md +6 -4
  119. package/templates/agent-docs/_manifest.json +23 -6
  120. package/templates/agent-docs/ai.md +370 -0
  121. package/templates/agent-docs/authentication.md +313 -31
  122. package/templates/agent-docs/caching.md +6 -0
  123. package/templates/agent-docs/cli.md +853 -50
  124. package/templates/agent-docs/data.md +608 -12
  125. package/templates/agent-docs/database/migrations.md +223 -25
  126. package/templates/agent-docs/database/misc.md +156 -40
  127. package/templates/agent-docs/database/querying.md +19 -1
  128. package/templates/agent-docs/database/scaling.md +60 -0
  129. package/templates/agent-docs/database/schema.md +1 -0
  130. package/templates/agent-docs/database/seedsdialects.md +208 -19
  131. package/templates/agent-docs/database/transactions.md +68 -0
  132. package/templates/agent-docs/deployment.md +284 -25
  133. package/templates/agent-docs/introduction.md +88 -17
  134. package/templates/agent-docs/local-first-mobile.md +79 -4
  135. package/templates/agent-docs/multi-tenancy.md +188 -42
  136. package/templates/agent-docs/observability.md +58 -3
  137. package/templates/agent-docs/plugins/ai-flows.md +247 -2
  138. package/templates/agent-docs/plugins/analytics-postgres.md +1 -1
  139. package/templates/agent-docs/plugins/audit.md +37 -1
  140. package/templates/agent-docs/plugins/auth-social.md +143 -0
  141. package/templates/agent-docs/plugins/auth-workos.md +4 -2
  142. package/templates/agent-docs/plugins/auth.md +131 -6
  143. package/templates/agent-docs/plugins/billing.md +132 -15
  144. package/templates/agent-docs/plugins/cdc-out.md +46 -7
  145. package/templates/agent-docs/plugins/clickhouse.md +32 -1
  146. package/templates/agent-docs/plugins/duckdb.md +1 -1
  147. package/templates/agent-docs/plugins/flags.md +132 -0
  148. package/templates/agent-docs/plugins/governance.md +105 -7
  149. package/templates/agent-docs/plugins/multitenancy.md +9 -4
  150. package/templates/agent-docs/plugins/presence.md +13 -2
  151. package/templates/agent-docs/plugins/ratelimit.md +9 -0
  152. package/templates/agent-docs/plugins/search.md +162 -8
  153. package/templates/agent-docs/plugins/sso-saml.md +47 -8
  154. package/templates/agent-docs/plugins/storage.md +11 -0
  155. package/templates/agent-docs/plugins/webhooks.md +105 -0
  156. package/templates/agent-docs/plugins.md +152 -18
  157. package/templates/agent-docs/reference.md +60 -3
  158. package/templates/agent-docs/releases.md +1117 -0
  159. package/templates/agent-docs/routing.md +43 -25
  160. package/templates/agent-docs/scheduling.md +27 -0
  161. package/templates/agent-docs/schema-driven-ui.md +92 -12
  162. package/templates/agent-docs/security.md +426 -0
  163. package/templates/agent-docs/templates/apibackends.md +87 -18
  164. package/templates/agent-docs/templates/appshells.md +32 -14
  165. package/templates/agent-docs/templates/overview.md +13 -8
  166. package/templates/agent-docs/testing.md +211 -14
  167. package/templates/agent-docs/whats-new.md +98 -136
  168. package/templates/agent-docs/workflows.md +231 -14
  169. package/templates/apps/api-ai/actions/summarize.action.ts +11 -0
  170. package/templates/apps/api-ai/package.json +8 -7
  171. package/templates/apps/api-auth/actions/me.action.ts +13 -0
  172. package/templates/apps/api-auth/package.json +9 -8
  173. package/templates/apps/api-backend/mutations/notes.create.mutation.ts +9 -0
  174. package/templates/apps/api-backend/package.json +12 -8
  175. package/templates/apps/api-backend/queries/notes.query.ts +30 -8
  176. package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +14 -0
  177. package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +8 -0
  178. package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +10 -0
  179. package/templates/apps/api-backend-deactivation/package.json +8 -7
  180. package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +17 -0
  181. package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +9 -0
  182. package/templates/apps/api-backend-mail/package.json +9 -8
  183. package/templates/apps/api-backend-mail/queries/notes.query.ts +30 -8
  184. package/templates/apps/api-backend-mariadb/.env.example +14 -0
  185. package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +9 -0
  186. package/templates/apps/api-backend-mariadb/package.json +10 -9
  187. package/templates/apps/api-backend-mariadb/queries/notes.query.ts +30 -8
  188. package/templates/apps/api-backend-sqlite/.env.example +14 -0
  189. package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +9 -0
  190. package/templates/apps/api-backend-sqlite/package.json +9 -8
  191. package/templates/apps/api-backend-sqlite/queries/notes.query.ts +30 -8
  192. package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +13 -0
  193. package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +12 -0
  194. package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +9 -0
  195. package/templates/apps/api-backend-storage/package.json +9 -8
  196. package/templates/apps/api-backend-storage/queries/notes.query.ts +30 -8
  197. package/templates/apps/api-cms/actions/content.get.action.ts +7 -0
  198. package/templates/apps/api-cms/actions/content.types.action.ts +6 -0
  199. package/templates/apps/api-cms/actions/me.action.ts +13 -0
  200. package/templates/apps/api-cms/app.config.ts +19 -0
  201. package/templates/apps/api-cms/authz.ts +63 -0
  202. package/templates/apps/api-cms/mutations/content.publish.mutation.ts +10 -0
  203. package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +10 -0
  204. package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +5 -0
  205. package/templates/apps/api-cms/package.json +11 -10
  206. package/templates/apps/api-cms/queries/content.list.query.ts +23 -7
  207. package/templates/apps/api-cms/tests/accessDecisions.test.ts +121 -0
  208. package/templates/apps/api-cms/tests/content.descriptors.test.ts +8 -4
  209. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +8 -0
  210. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +13 -0
  211. package/templates/apps/api-collab/package.json +9 -8
  212. package/templates/apps/api-collab/queries/documents.query.ts +19 -7
  213. package/templates/apps/api-data-advanced/package.json +9 -8
  214. package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +12 -0
  215. package/templates/apps/api-data-advanced/queries/books.search.query.ts +9 -0
  216. package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +17 -0
  217. package/templates/apps/api-durable/mutations/orders.place.mutation.ts +9 -0
  218. package/templates/apps/api-durable/package.json +9 -8
  219. package/templates/apps/api-feature-flags/actions/notes.export.action.ts +16 -0
  220. package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +12 -0
  221. package/templates/apps/api-feature-flags/package.json +10 -9
  222. package/templates/apps/api-governance/README.md +30 -8
  223. package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -1
  224. package/templates/apps/api-governance/actions/profiles.get.action.ts +29 -4
  225. package/templates/apps/api-governance/database/schema.ts +16 -4
  226. package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +12 -0
  227. package/templates/apps/api-governance/package.json +9 -8
  228. package/templates/apps/api-kv/actions/sync.pull.action.ts +15 -0
  229. package/templates/apps/api-kv/actions/sync.reset.action.ts +13 -0
  230. package/templates/apps/api-kv/actions/sync.status.action.ts +7 -0
  231. package/templates/apps/api-kv/package.json +9 -8
  232. package/templates/apps/api-kv/queries/events.list.query.ts +19 -8
  233. package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +11 -0
  234. package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +13 -0
  235. package/templates/apps/api-moderation/package.json +9 -8
  236. package/templates/apps/api-observability/mutations/notes.create.mutation.ts +8 -0
  237. package/templates/apps/api-observability/package.json +9 -8
  238. package/templates/apps/api-observability/queries/notes.list.query.ts +13 -0
  239. package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +14 -0
  240. package/templates/apps/api-ratelimit/package.json +9 -8
  241. package/templates/apps/api-rbac/package.json +9 -8
  242. package/templates/apps/api-rest/package.json +8 -7
  243. package/templates/apps/api-saas/mutations/projects.create.mutation.ts +13 -0
  244. package/templates/apps/api-saas/package.json +12 -11
  245. package/templates/apps/api-saas/queries/projects.list.query.ts +11 -0
  246. package/templates/apps/api-saas-starter/actions/me.action.ts +13 -0
  247. package/templates/apps/api-saas-starter/app.config.ts +19 -0
  248. package/templates/apps/api-saas-starter/authz.ts +75 -0
  249. package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +10 -0
  250. package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +9 -0
  251. package/templates/apps/api-saas-starter/package.json +15 -11
  252. package/templates/apps/api-saas-starter/queries/invites.list.query.ts +19 -8
  253. package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -7
  254. package/templates/apps/api-saas-starter/tests/accessDecisions.test.ts +135 -0
  255. package/templates/apps/api-search/mutations/articles.create.mutation.ts +14 -0
  256. package/templates/apps/api-search/package.json +9 -8
  257. package/templates/apps/api-search/queries/articles.list.query.ts +21 -8
  258. package/templates/apps/api-status/README.md +10 -3
  259. package/templates/apps/api-status/app.config.ts +8 -3
  260. package/templates/apps/api-status/authz.ts +5 -3
  261. package/templates/apps/api-status/package.json +9 -8
  262. package/templates/apps/api-status/queries/components.list.query.ts +14 -6
  263. package/templates/apps/api-status/queries/incidents.live.query.ts +23 -10
  264. package/templates/apps/api-status/queries/updates.list.query.ts +16 -9
  265. package/templates/apps/api-status/tests/status.test.ts +9 -1
  266. package/templates/apps/api-versioning/actions/documents.asOf.action.ts +12 -0
  267. package/templates/apps/api-versioning/actions/documents.history.action.ts +13 -0
  268. package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +9 -0
  269. package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +12 -0
  270. package/templates/apps/api-versioning/package.json +9 -8
  271. package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +16 -0
  272. package/templates/apps/api-webhooks/package.json +10 -9
  273. package/templates/apps/api-webhooks/queries/orders.list.query.ts +10 -0
  274. package/templates/apps/changelog/package.json +8 -6
  275. package/templates/apps/edge-functions/package.json +2 -2
  276. package/templates/apps/frontend-admin/package.json +10 -8
  277. package/templates/apps/frontend-admin/src/lib/admin.ts +20 -9
  278. package/templates/apps/frontend-admin/src/locales/de.ts +11 -1
  279. package/templates/apps/frontend-admin/src/locales/en.ts +13 -1
  280. package/templates/apps/frontend-admin/src/pages/admin/[entity]/page.tsx +65 -22
  281. package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +130 -23
  282. package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +3 -2
  283. package/templates/apps/frontend-admin/src/pages/admin/page.test.tsx +19 -2
  284. package/templates/apps/frontend-admin/src/pages/admin/page.tsx +9 -4
  285. package/templates/apps/frontend-app/app.config.ts +4 -3
  286. package/templates/apps/frontend-app/package.json +11 -8
  287. package/templates/apps/frontend-app/src/lib/api.ts +25 -0
  288. package/templates/apps/frontend-app/src/pages/page.test.tsx +130 -82
  289. package/templates/apps/frontend-app/src/pages/page.tsx +14 -18
  290. package/templates/apps/frontend-auth/package.json +10 -8
  291. package/templates/apps/frontend-blank/package.json +9 -7
  292. package/templates/apps/frontend-cms/package.json +11 -9
  293. package/templates/apps/frontend-collab/package.json +12 -9
  294. package/templates/apps/frontend-collab/src/pages/page.test.tsx +122 -78
  295. package/templates/apps/frontend-contact/package.json +9 -7
  296. package/templates/apps/frontend-dashboard/package.json +9 -7
  297. package/templates/apps/frontend-docs/package.json +9 -7
  298. package/templates/apps/frontend-i18n/package.json +8 -6
  299. package/templates/apps/frontend-landing/package.json +9 -7
  300. package/templates/apps/frontend-portal/package.json +10 -8
  301. package/templates/apps/frontend-saas/app.config.ts +10 -6
  302. package/templates/apps/frontend-saas/package.json +10 -8
  303. package/templates/apps/frontend-saas/src/lib/api.ts +27 -32
  304. package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +7 -8
  305. package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +27 -3
  306. package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +4 -4
  307. package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +3 -4
  308. package/templates/apps/frontend-spa/package.json +9 -7
  309. package/templates/apps/frontend-ssr/package.json +9 -7
  310. package/templates/apps/frontend-ssr-api/package.json +10 -8
  311. package/templates/apps/frontend-static-blog/package.json +8 -6
  312. package/templates/apps/frontend-status/package.json +10 -8
  313. package/templates/apps/mobile-app/README.md +1 -0
  314. package/templates/apps/mobile-app/package.json +4 -2
  315. package/templates/apps/mobile-app/src/app/index.tsx +22 -12
  316. package/templates/apps/mobile-app/src/app/orders/[id].tsx +1 -1
  317. package/templates/apps/mobile-app/src/lib/api.ts +34 -0
  318. package/templates/apps/mobile-app/voltro.mobile.ts +4 -2
  319. package/templates/baselines/bare/.env.example +14 -0
  320. package/templates/baselines/bare/baseline.json +4 -4
  321. package/templates/baselines/compose/.env.example +14 -0
  322. package/templates/baselines/compose/README.md +1 -1
  323. package/templates/baselines/compose/baseline.json +5 -5
  324. package/templates/baselines/compose-mariadb/.env.example +14 -0
  325. package/templates/baselines/compose-mariadb/README.md +1 -1
  326. package/templates/baselines/compose-mariadb/baseline.json +5 -5
  327. package/templates/baselines/helm/.env.example +14 -0
  328. package/templates/baselines/helm/baseline.json +4 -4
  329. package/dist/apiBuild-C-x9YacA.js +0 -2
  330. package/dist/appGraph-CvQCte0z.js +0 -2
  331. package/dist/checkCommand-DRovTKza.js +0 -2
  332. package/dist/commands-CJfepbm4.js +0 -11541
  333. package/dist/dbCommand-b1gum4td.js +0 -2
  334. package/dist/dev-iiMtlkfs.js +0 -3
  335. package/dist/devActivity-BhIu6ncs.js +0 -159
  336. package/dist/frameworkTableAssembly-BwIrO5nv.js +0 -638
  337. package/dist/frameworkTableAssembly-D-EebUQX.js +0 -2
  338. package/dist/inspect-mmBuRXmy.js +0 -2
  339. package/dist/manifestBuild-Dj8Jjoto.js +0 -2
  340. package/dist/seedRunner-Bqxgp7HZ.js +0 -230
  341. package/dist/serveCommand-DdaM4Hup.js +0 -1608
  342. package/dist/start-C0koT0UO.js +0 -1084
  343. /package/templates/apps/api-ai/actions/{summarize.action.server.tsx → summarize.action.server.ts} +0 -0
@@ -22,6 +22,35 @@ Please report suspected vulnerabilities **privately** — never in public issues
22
22
 
23
23
  You'll get an **acknowledgement within 3 business days**, an assessment once we've reproduced the issue, and a **coordinated disclosure** timeline agreed with you — with credit in the release notes if you'd like it. We don't run a paid bug-bounty program yet, but we genuinely value responsible disclosure.
24
24
 
25
+ ## Procedures are default-DENY
26
+
27
+ Every wire-exposed procedure must declare **who may call it**. A `*.query.ts` /
28
+ `*.mutation.ts` / `*.action.ts` / `*.stream.ts` that declares neither `guards:`
29
+ nor `openAccess:` is refused at boot — by `voltro dev`, by `voltro serve`, and by
30
+ `voltro doctor` as a preflight.
31
+
32
+ Before this, `guards:` defaulted to "allowed": a discovered procedure with no
33
+ guard was callable by any authenticated session. Nothing in the code said so,
34
+ and the only structural check was a report a human had to run.
35
+
36
+ ```ts
37
+ guards: [{ scope: 'invoices:read' }] // the caller must hold a scope
38
+ openAccess: 'public pricing, no caller data' // anyone may call it — and why
39
+ internal: true // not on the wire at all
40
+ ```
41
+
42
+ `openAccess` takes a reason rather than a boolean on purpose: without an explicit
43
+ way to declare an endpoint open, the only way to satisfy the gate is to invent a
44
+ guard, and the guard people invent is one every caller already holds. That reads
45
+ as protection and enforces nothing.
46
+
47
+ The whole app can opt out with one field — `security: { defaultDeny: false }` in
48
+ `app.config.ts`. There is no environment variable for it: the only direction
49
+ anyone reaches for is off, and an env var is how that becomes permanent in one CI
50
+ job with no diff to review. Full detail — including how plugin routes are
51
+ covered per-request in the dispatch spine rather than by the boot gate — in
52
+ [Authorization](/docs/authentication/authorization).
53
+
25
54
  ## Outbound HTTP is SSRF-guarded by default
26
55
 
27
56
  The `HttpClient` your handlers `yield*` refuses internal targets:
@@ -88,6 +117,403 @@ DNS is not resolved. A public hostname that *resolves* to a private address (DNS
88
117
  rebinding) still passes. That vector needs network-layer egress control; it is
89
118
  stated here rather than silently implied.
90
119
 
120
+ ## Where the transport settings live
121
+
122
+ Everything in the next three sections — the origin check, which proxies may
123
+ forward a client IP, and the response headers — is one `security:` block in
124
+ `app.config.ts`, read identically by `voltro dev` and `voltro serve`:
125
+
126
+ ```ts
127
+ // app.config.ts
128
+ export default {
129
+ security: {
130
+ // Cross-site protection for every state-changing request. Default 'same-origin'.
131
+ originGuard: 'same-origin',
132
+ // Extra origins to accept — a split web/api deployment.
133
+ allowedOrigins: ['https://app.example.com'],
134
+ // Whose x-forwarded-for to believe. Empty (the default) ignores the header.
135
+ trustedProxies: ['private'],
136
+ // Security response headers. On by default; 'strict' applies the api policy to HTML too.
137
+ headers: { mode: 'default' },
138
+ },
139
+ }
140
+ ```
141
+
142
+ Every field has a safe default, so an app that writes none of this is already
143
+ protected. Each also has an environment override, listed with its section below
144
+ — use those to correct a deployment you cannot rebuild, and the config file for
145
+ everything else. A security decision that exists only as an exported variable on
146
+ one machine is invisible to code review.
147
+
148
+ ## Cross-site requests are refused
149
+
150
+ **Every request that can change state is checked** — anything but `GET`, `HEAD`
151
+ and `OPTIONS` — plus the `/ws` upgrade, which is a GET. That is deliberately a
152
+ rule about the METHOD rather than a list of paths, because a mutation reaches
153
+ your api by more than one road: `POST /rpc`, every REST route projected from a
154
+ `publicApi:` mutation, everything in `apiConfig.restRoutes`, and
155
+ `POST /v1/api-keys`. They all resolve the caller through the same auth chain,
156
+ so they are all reachable **with the session cookie automatically attached**.
157
+
158
+ A browser request is accepted when its `Origin` matches the `Host` it was
159
+ addressed to, or is in your allowlist. Anything else gets `403 origin not
160
+ allowed` — including `Origin: null`, which is what a sandboxed iframe sends, and
161
+ a request with no `Origin` whose `Sec-Fetch-Site` says `cross-site`.
162
+
163
+ Before this, framework-wide CSRF protection rested entirely on the
164
+ `SameSite=Lax` cookie default — one caller-overridable attribute that still
165
+ permits top-level-navigation POST, and that does nothing at all for a
166
+ bearer/JWT flow.
167
+
168
+ Reads are not checked: a `GET` cannot be a cross-site write, and checking it
169
+ would break every link into your api.
170
+
171
+ ### A split web/api deployment must declare its origins
172
+
173
+ If your web app is served from a different origin than your api, say so — or
174
+ every mutation, every REST write and every socket from that page will 403:
175
+
176
+ ```ts
177
+ // app.config.ts
178
+ export default {
179
+ security: {
180
+ allowedOrigins: ['https://app.example.com', 'https://admin.example.com'],
181
+ },
182
+ }
183
+ ```
184
+
185
+ Same-origin apps (the default layout, and anything behind one ingress) need no
186
+ change. `security: { originGuard: 'off' }` disables the check; a misspelled
187
+ value falls back to **enforcing**, never to off.
188
+
189
+ Environment overrides, for a deployment you cannot rebuild:
190
+ `VOLTRO_ALLOWED_ORIGINS` (comma-separated) and `VOLTRO_ORIGIN_GUARD=off`. If you
191
+ embed the runtime yourself, the same values are
192
+ `RpcServerOptions.security.originGuard` / `.allowedOrigins`.
193
+
194
+ ### Server-to-server callers are exempt, and that is deliberate
195
+
196
+ A request carrying neither `Origin` nor `Sec-Fetch-Site` did not come from a
197
+ browsing context, so it is allowed. That covers:
198
+
199
+ - **SSR loaders** — `voltro dev` / `voltro start` render pages server-side and
200
+ their loaders call the api from node, which attaches no origin header. Without
201
+ this exemption, server-side first paint would break on every page with a
202
+ loader.
203
+ - mobile SDKs, `curl`, and any other service calling your api.
204
+
205
+ A CSRF attack needs the victim's ambient credentials, and only a browser attaches
206
+ those — a browser cannot be made to omit `Origin` on a cross-origin POST or a
207
+ WebSocket handshake. An attacker's own server can POST without one, but it has no
208
+ session to ride: that is simply an unauthenticated request, and auth and guards
209
+ still apply to it.
210
+
211
+ Origins are compared by **authority** (host + port), not scheme. Behind a
212
+ TLS-terminating ingress your app sees plain http while the browser reports
213
+ `https://…`, and there is no unforgeable way to learn the external scheme.
214
+
215
+ ### Local development is not a special case you have to configure
216
+
217
+ When **both** the origin and the `Host` are loopback (`localhost`, `127.0.0.1`,
218
+ `*.localhost`), the request is accepted. That is the `voltro dev` layout: the web
219
+ dev server proxies the api with `changeOrigin: true`, so the api sees
220
+ `Host: localhost:4000` while the browser correctly reports
221
+ `Origin: http://localhost:5190`. Strictly compared those differ, and every dev
222
+ session would lose its websocket.
223
+
224
+ Both sides must be loopback. A production api on `api.example.com` still refuses
225
+ `Origin: http://localhost:5190` — a browser cannot be made to claim a loopback
226
+ origin it is not on.
227
+
228
+ **Testing a dev server from a phone on your wifi is the one case that needs a
229
+ line.** The page is then `http://192.168.1.5:5190`, which is not loopback, so the
230
+ socket is refused and the api logs `refused cross-site request` naming the
231
+ origin. Add it while you test:
232
+
233
+ ```ts
234
+ // app.config.ts
235
+ export default {
236
+ security: {
237
+ allowedOrigins: ['http://192.168.1.5:5190'],
238
+ },
239
+ }
240
+ ```
241
+
242
+ The carve-out is deliberately not widened to "both sides are private
243
+ addresses": on a self-hosted internal deployment that would accept any other
244
+ host on the same network, which is a different claim than "the attacker already
245
+ runs code on this machine".
246
+
247
+ ### Routes that a third party legitimately POSTs to
248
+
249
+ A few surfaces are outside the check, because their caller cannot be a browser
250
+ riding your user's cookie:
251
+
252
+ - **The inspect surface** (`/_voltro/inspect/*`) — token-gated, and meant to be
253
+ read cross-origin by the local and cloud dashboards.
254
+ - **Incoming webhooks** (`*.webhook.tsx`) — authenticated by the sender's
255
+ signature, which the framework refuses to boot without.
256
+ - **Plugin routes that declare it.** First-party examples:
257
+ `@voltro/plugin-sso-saml` (`/saml` — the IdP makes the browser form-POST a
258
+ signed assertion, which IS a cross-site POST), `@voltro/plugin-storage`'s
259
+ upload routes (a signed upload ticket, plus their own CORS allowlist),
260
+ `@voltro/plugin-billing`'s webhook and `@voltro/plugin-scim` (bearer-only).
261
+
262
+ If you write a plugin HTTP route in that category, declare it:
263
+
264
+ ```ts
265
+ import type { PluginHttpRoute } from '@voltro/protocol'
266
+
267
+ const route: PluginHttpRoute = {
268
+ method: 'POST',
269
+ path: '/partner/callback',
270
+ // The signature on the body is the authority — not the session cookie.
271
+ originGuard: 'exempt',
272
+ handle: async (req) => {
273
+ if (!verifySignature(req)) return { status: 401 }
274
+ return { status: 204 }
275
+ },
276
+ }
277
+ ```
278
+
279
+ Apply exactly one test before you write that line: *if an attacker's page makes
280
+ a browser send this request with your user's cookies attached, does anything
281
+ happen?* If the answer is "no — it still needs a signature, a bearer token or a
282
+ signed ticket the attacker does not have", the route is exempt. Otherwise it is
283
+ not. The exemption covers the route's whole path prefix, not the sub-paths its
284
+ handler branches on.
285
+
286
+ ## The client IP comes from the socket, not from a header
287
+
288
+ `x-forwarded-for` is a request header, so **any client can write it**. Voltro
289
+ ignores it unless you declare which proxies are allowed to forward — the address
290
+ used for rate limiting, geo-blocking and audit records is
291
+ `socket.remoteAddress`, the one value nobody upstream of the kernel can forge.
292
+
293
+ If you run behind a load balancer, ingress or CDN **and** rate-limit or geo-block
294
+ per IP, declare it:
295
+
296
+ ```ts
297
+ // app.config.ts
298
+ export default {
299
+ security: {
300
+ trustedProxies: ['private'], // RFC1918 + CGNAT + link-local + unique-local
301
+ // ['loopback'] — local / docker-compose
302
+ // ['10.0.0.0/8', 'fc00::/7'] — explicit CIDRs
303
+ // ['2'] — trust two hops
304
+ // ['*'] — any peer; only when your ingress
305
+ // OVERWRITES rather than appends
306
+ },
307
+ }
308
+ ```
309
+
310
+ The environment override is `VOLTRO_TRUSTED_PROXIES`, comma-separated
311
+ (`VOLTRO_TRUSTED_PROXIES=10.0.0.0/8,fc00::/7`).
312
+
313
+ With a list configured, the peer that opened the connection must itself be
314
+ trusted, and the chain is walked right-to-left past every declared proxy — so a
315
+ client that prepends a fake hop cannot push the resolved address further left.
316
+ The same setting decides whether `x-forwarded-proto` is believed, which is what
317
+ gates HSTS below.
318
+
319
+ Without it, a per-IP limiter still works; it just counts every request against
320
+ your proxy's address instead of the caller's. Embedders pass the same list as
321
+ `RpcServerOptions.security.trustedProxies`.
322
+
323
+ ### The same address reaches your plugin routes
324
+
325
+ A plugin HTTP route receives it as `req.remoteAddr` — already resolved through
326
+ the policy above. **Use that, never `req.headers['x-forwarded-for']`.** The
327
+ built-in auth routes do: what `@voltro/plugin-auth` and
328
+ `@voltro/plugin-auth-social` write into `sessions.ipAddress` is the resolved
329
+ address, so the column a breach investigation reads cannot be chosen by the
330
+ caller.
331
+
332
+ ```ts
333
+ import type { PluginHttpRoute } from '@voltro/protocol'
334
+
335
+ const route: PluginHttpRoute = {
336
+ method: 'POST',
337
+ path: '/partner/callback',
338
+ handle: async (req) => {
339
+ // Resolved once per request; `undefined` only when there is no socket
340
+ // address (a unix socket, or a hand-built request in a test).
341
+ await audit(req.remoteAddr ?? null)
342
+ return { status: 204 }
343
+ },
344
+ }
345
+ ```
346
+
347
+ ## Security headers ship by default
348
+
349
+ Every response from the api listener carries:
350
+
351
+ ```
352
+ content-security-policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'
353
+ x-frame-options: DENY
354
+ referrer-policy: no-referrer
355
+ x-content-type-options: nosniff
356
+ strict-transport-security: max-age=15552000; includeSubDomains (https only)
357
+ ```
358
+
359
+ `default-src 'none'` is safe here because this listener serves the **api** —
360
+ rpc JSON, the socket upgrade, inspect routes, webhooks, plugin routes — not your
361
+ web app's HTML, which is a different listener. A `text/html` response from a
362
+ plugin route gets a relaxed policy instead
363
+ (`frame-ancestors 'none'; base-uri 'none'; object-src 'none'`), so a docs page
364
+ still renders while clickjacking and base-tag injection stay closed.
365
+
366
+ HSTS is sent **only over https** and deliberately never carries `preload`:
367
+ preload is effectively irreversible for a domain, so it has to be your decision.
368
+ `Cross-Origin-Opener-Policy` and `Cross-Origin-Resource-Policy` are not defaulted
369
+ either — guessing them breaks a legitimate cross-origin dashboard — but you can
370
+ add them.
371
+
372
+ A route that sets a header itself always wins; the framework only fills gaps.
373
+
374
+ The whole policy is one field, including an `extra` bag for headers the
375
+ framework does not default:
376
+
377
+ ```ts
378
+ // app.config.ts
379
+ export default {
380
+ security: {
381
+ headers: {
382
+ mode: 'default', // 'off' | 'default' | 'strict'
383
+ hsts: 'max-age=31536000; includeSubDomains', // or false to drop it
384
+ extra: { 'permissions-policy': 'geolocation=()' },
385
+ },
386
+ },
387
+ }
388
+ ```
389
+
390
+ `mode: 'strict'` applies the api policy to HTML too. That is the documented
391
+ trade: it blanks `@voltro/plugin-openapi`'s `/docs` page, which loads its viewer
392
+ from a CDN. Choose it when this listener serves no HTML you own.
393
+
394
+ Environment overrides: `VOLTRO_SECURITY_HEADERS` (`off|default|strict`),
395
+ `VOLTRO_CSP`, `VOLTRO_CSP_HTML`, `VOLTRO_HSTS` — each accepting `off` to drop
396
+ just that one. Embedders pass the same object as
397
+ `RpcServerOptions.security.headers`.
398
+
399
+ ## Incoming webhooks must verify their caller
400
+
401
+ An incoming webhook is a **public POST that runs your application code**. A
402
+ `defineIncomingWebhook({...})` that declares no signature scheme now refuses to
403
+ boot, naming the endpoint.
404
+
405
+ ```ts
406
+ export default defineIncomingWebhook({
407
+ id: 'orders.paid',
408
+ provider: stripeWebhookProvider(), // HMAC scheme + replay window + idempotency
409
+ payload: OrderPaid,
410
+ handler: async (ctx) => { /* the caller is already verified here */ },
411
+ })
412
+ ```
413
+
414
+ Four ways to satisfy it:
415
+
416
+ | Declaration | Means |
417
+ |---|---|
418
+ | `provider: stripeWebhookProvider()` | a preset brings the scheme, replay window and idempotency key |
419
+ | `signature: { _tag: 'hmac', … }` | a hand-declared scheme for a sender with its own convention |
420
+ | `verification: 'provider'` | your handler verifies with the provider's own SDK |
421
+ | `verification: 'none'` | deliberately public — a gateway or IP allow-list owns the boundary; logged as a warning on every boot |
422
+
423
+ A signature-verified webhook reads its shared secret from an env var named after
424
+ its id:
425
+
426
+ ```bash
427
+ VOLTRO_WEBHOOK_SECRET_ORDERS_PAID=<the value the sender holds>
428
+ ```
429
+
430
+ **A missing secret is now a 503, not a skipped check.** It used to run the
431
+ handler unverified, so forgetting one variable silently converted a verified
432
+ webhook into an open one — in production, with nothing in the code to review.
433
+ The framework never invents this value: the sender holds the other half, so a
434
+ generated secret would authenticate nobody.
435
+
436
+ ### `voltro doctor` tells you before a deploy does
437
+
438
+ The preflight reports the same verdict the boot reaches:
439
+
440
+ ```
441
+ incoming webhook verification
442
+ ✓ signature / provider verified 2
443
+ ! deliberately public (none) 1
444
+ internal.sync — `verification: 'none'`; a gateway / IP allow-list owns this URL
445
+ ✗ nothing verifies the caller 0
446
+ ```
447
+
448
+ An endpoint in the last row exits non-zero — that app does not start. The middle
449
+ row never fails; it is there so a deliberately public URL is visible in a review
450
+ instead of only in a boot log. Both rows are in `voltro doctor --json` under
451
+ `webhookVerification`.
452
+
453
+ ## The rpc body cap counts bytes as they arrive — and answers 413 either way
454
+
455
+ `POST /rpc` is capped at 8 MiB (`VOLTRO_MAX_RPC_BODY_BYTES`). The limit is
456
+ enforced **while the body streams**, so a `Transfer-Encoding: chunked` request
457
+ with no `Content-Length` is cut off at the cap rather than buffered without
458
+ bound. A declared oversize length is still rejected up front, so an honest client
459
+ gets its `413` without uploading anything.
460
+
461
+ **Both shapes end in the same response:**
462
+
463
+ | Request | Response |
464
+ | --- | --- |
465
+ | `Content-Length` over the cap | `413 Payload Too Large`, before the upload |
466
+ | `Transfer-Encoding: chunked` over the cap | `413 Payload Too Large`, once the counter crosses the cap |
467
+ | Either, under the cap | handled normally |
468
+
469
+ The refusal is logged server-side under the `voltro:security` scope, with the cap
470
+ and the number of bytes read before the server stopped (never the body's real
471
+ size — it is not read).
472
+
473
+ Uploads ride separate plugin routes with their own `limits.maxBytes`, and are
474
+ unaffected.
475
+
476
+ ## Logs are redacted by default
477
+
478
+ Every logger surface — `createLogger`, `makeLogger`, `LoggerLayer` — installs a redactor with no configuration, and it runs **before** formatting and **before** the sink fan-out, so a masked value reaches neither stdout nor any downstream sink (the CLI buffer, logship, Datadog).
479
+
480
+ ```ts
481
+ import { createLogger } from '@voltro/logger'
482
+
483
+ const log = createLogger({ scope: 'orders' })
484
+ log.info('request', { headers: req.headers, body: input })
485
+ // → headers.authorization = [redacted]
486
+ // headers.cookie = [redacted]
487
+ // body.newPassword = [redacted]
488
+ // headers['user-agent'] = curl/8 ← untouched
489
+ ```
490
+
491
+ Three rules, all on:
492
+
493
+ - **Key names** (`DEFAULT_REDACT_KEYS`) — matched exactly on a normalised key, so `apiKey`, `api_key`, `API-KEY` and `Api Key` are one key. Credentials, bearer/API tokens, `authorization` / `cookie` / `set-cookie` / `x-api-key`, session and second-factor values, and the payment / government identifiers (`creditCard`, `cvv`, `iban`, `ssn`, …) that must never reach a log index.
494
+ - **Key fragments** (`DEFAULT_REDACT_KEY_PARTS`) — substring matches for the compound names real code writes: `newPassword`, `oldPassword`, `stripeApiKey`, `userAccessToken`.
495
+ - **Value shapes** — a credential is masked regardless of its key: `Bearer …`, `Basic …`, a JWT, `sk_` / `pk_` / `whsec_`-prefixed keys, GitHub / Slack / AWS key ids, a PEM private key.
496
+
497
+ There is deliberately **no entropy heuristic**. A trace id, a content hash, a git sha and a base64 thumbnail are all long and high-entropy — a redactor that eats the fields an incident is read through gets switched off wholesale, which costs more than the gap it closed.
498
+
499
+ ### Adding to the list, and why nothing subtracts
500
+
501
+ `redactKeys` **adds**; it does not replace:
502
+
503
+ ```ts
504
+ createLogger({ redactKeys: ['patientId', 'policyNumber'] })
505
+ ```
506
+
507
+ There is no way to remove a built-in key. The only reason to un-mask `password` is to read it while debugging, and the answer to that is to log a non-secret projection — not to publish the value to stdout and to whatever log shipper the deployment happens to have. A subtraction knob would also apply to every **dependency** logging under that key, including plugins you never read, which is a blast radius an app cannot assess.
508
+
509
+ The deliberate escape hatch is `redact`, a transform you write and own — and it runs *after* the built-in redactor, so it can mask more and structurally cannot unmask:
510
+
511
+ ```ts
512
+ createLogger({ redact: (record) => ({ ...record, fields: { ...record.fields, region: 'eu' } }) })
513
+ ```
514
+
515
+ > **If a log line you relied on went quiet**, rename the field rather than reaching for an exemption. `traceId`, `requestId` and `cookieName` are all untouched — the framework's own session diagnostic was renamed from `cookie` to `cookieName` for exactly this reason.
516
+
91
517
  ## Auditing what your log tables actually hold
92
518
 
93
519
  ```bash