@fuzdev/fuz_app 0.55.0 → 0.57.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 (333) hide show
  1. package/dist/actions/CLAUDE.md +211 -155
  2. package/dist/actions/action_bridge.d.ts +8 -5
  3. package/dist/actions/action_bridge.d.ts.map +1 -1
  4. package/dist/actions/action_bridge.js +1 -11
  5. package/dist/actions/action_codegen.d.ts +19 -0
  6. package/dist/actions/action_codegen.d.ts.map +1 -1
  7. package/dist/actions/action_codegen.js +20 -14
  8. package/dist/actions/action_registry.d.ts.map +1 -1
  9. package/dist/actions/action_registry.js +5 -2
  10. package/dist/actions/action_rpc.d.ts +110 -44
  11. package/dist/actions/action_rpc.d.ts.map +1 -1
  12. package/dist/actions/action_rpc.js +92 -287
  13. package/dist/actions/action_spec.d.ts +55 -16
  14. package/dist/actions/action_spec.d.ts.map +1 -1
  15. package/dist/actions/action_spec.js +16 -11
  16. package/dist/actions/action_types.d.ts +28 -60
  17. package/dist/actions/action_types.d.ts.map +1 -1
  18. package/dist/actions/action_types.js +13 -5
  19. package/dist/actions/broadcast_api.d.ts +2 -2
  20. package/dist/actions/broadcast_api.js +2 -2
  21. package/dist/actions/compile_action_registry.d.ts +50 -0
  22. package/dist/actions/compile_action_registry.d.ts.map +1 -0
  23. package/dist/actions/compile_action_registry.js +69 -0
  24. package/dist/actions/heartbeat.d.ts +8 -4
  25. package/dist/actions/heartbeat.d.ts.map +1 -1
  26. package/dist/actions/heartbeat.js +5 -4
  27. package/dist/actions/perform_action.d.ts +145 -0
  28. package/dist/actions/perform_action.d.ts.map +1 -0
  29. package/dist/actions/perform_action.js +258 -0
  30. package/dist/actions/register_action_ws.d.ts +44 -38
  31. package/dist/actions/register_action_ws.d.ts.map +1 -1
  32. package/dist/actions/register_action_ws.js +101 -159
  33. package/dist/actions/register_ws_endpoint.d.ts +2 -10
  34. package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
  35. package/dist/actions/register_ws_endpoint.js +32 -10
  36. package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
  37. package/dist/actions/transports_ws_auth_guard.js +1 -1
  38. package/dist/actions/transports_ws_backend.d.ts +1 -1
  39. package/dist/actions/transports_ws_backend.js +1 -1
  40. package/dist/auth/CLAUDE.md +673 -442
  41. package/dist/auth/account_action_specs.d.ts +28 -7
  42. package/dist/auth/account_action_specs.d.ts.map +1 -1
  43. package/dist/auth/account_action_specs.js +7 -7
  44. package/dist/auth/account_actions.d.ts +8 -14
  45. package/dist/auth/account_actions.d.ts.map +1 -1
  46. package/dist/auth/account_actions.js +26 -32
  47. package/dist/auth/account_queries.d.ts +46 -13
  48. package/dist/auth/account_queries.d.ts.map +1 -1
  49. package/dist/auth/account_queries.js +73 -33
  50. package/dist/auth/account_routes.d.ts +4 -3
  51. package/dist/auth/account_routes.d.ts.map +1 -1
  52. package/dist/auth/account_routes.js +58 -33
  53. package/dist/auth/account_schema.d.ts +46 -54
  54. package/dist/auth/account_schema.d.ts.map +1 -1
  55. package/dist/auth/account_schema.js +21 -48
  56. package/dist/auth/admin_action_specs.d.ts +55 -21
  57. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  58. package/dist/auth/admin_action_specs.js +42 -26
  59. package/dist/auth/admin_actions.d.ts +14 -21
  60. package/dist/auth/admin_actions.d.ts.map +1 -1
  61. package/dist/auth/admin_actions.js +47 -44
  62. package/dist/auth/audit_emitter.d.ts +160 -0
  63. package/dist/auth/audit_emitter.d.ts.map +1 -0
  64. package/dist/auth/audit_emitter.js +83 -0
  65. package/dist/auth/audit_log_queries.d.ts +17 -87
  66. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  67. package/dist/auth/audit_log_queries.js +17 -96
  68. package/dist/auth/audit_log_routes.d.ts +1 -1
  69. package/dist/auth/audit_log_routes.d.ts.map +1 -1
  70. package/dist/auth/audit_log_routes.js +7 -3
  71. package/dist/auth/audit_log_schema.d.ts +48 -42
  72. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  73. package/dist/auth/audit_log_schema.js +56 -43
  74. package/dist/auth/auth_guard_resolver.d.ts +44 -0
  75. package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
  76. package/dist/auth/auth_guard_resolver.js +56 -0
  77. package/dist/auth/bootstrap_account.d.ts +7 -7
  78. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  79. package/dist/auth/bootstrap_account.js +7 -7
  80. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  81. package/dist/auth/bootstrap_routes.js +11 -10
  82. package/dist/auth/cleanup.d.ts +20 -26
  83. package/dist/auth/cleanup.d.ts.map +1 -1
  84. package/dist/auth/cleanup.js +33 -47
  85. package/dist/auth/credential_type_schema.d.ts +115 -0
  86. package/dist/auth/credential_type_schema.d.ts.map +1 -0
  87. package/dist/auth/credential_type_schema.js +127 -0
  88. package/dist/auth/daemon_token_middleware.d.ts +1 -1
  89. package/dist/auth/daemon_token_middleware.js +3 -3
  90. package/dist/auth/ddl.d.ts +2 -2
  91. package/dist/auth/ddl.d.ts.map +1 -1
  92. package/dist/auth/ddl.js +6 -6
  93. package/dist/auth/deps.d.ts +7 -32
  94. package/dist/auth/deps.d.ts.map +1 -1
  95. package/dist/auth/grant_path_schema.d.ts +117 -0
  96. package/dist/auth/grant_path_schema.d.ts.map +1 -0
  97. package/dist/auth/grant_path_schema.js +137 -0
  98. package/dist/auth/invite_queries.d.ts +12 -1
  99. package/dist/auth/invite_queries.d.ts.map +1 -1
  100. package/dist/auth/invite_queries.js +12 -1
  101. package/dist/auth/invite_schema.d.ts +1 -1
  102. package/dist/auth/invite_schema.d.ts.map +1 -1
  103. package/dist/auth/invite_schema.js +1 -1
  104. package/dist/auth/middleware.d.ts.map +1 -1
  105. package/dist/auth/middleware.js +5 -2
  106. package/dist/auth/migrations.d.ts +22 -7
  107. package/dist/auth/migrations.d.ts.map +1 -1
  108. package/dist/auth/migrations.js +64 -25
  109. package/dist/auth/request_context.d.ts +157 -170
  110. package/dist/auth/request_context.d.ts.map +1 -1
  111. package/dist/auth/request_context.js +224 -268
  112. package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +130 -100
  113. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
  114. package/dist/auth/role_grant_offer_action_specs.js +262 -0
  115. package/dist/auth/role_grant_offer_actions.d.ts +104 -0
  116. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
  117. package/dist/auth/{permit_offer_actions.js → role_grant_offer_actions.js} +153 -140
  118. package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +80 -70
  119. package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
  120. package/dist/auth/role_grant_offer_notifications.js +182 -0
  121. package/dist/auth/{permit_offer_queries.d.ts → role_grant_offer_queries.d.ts} +64 -64
  122. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
  123. package/dist/auth/{permit_offer_queries.js → role_grant_offer_queries.js} +136 -123
  124. package/dist/auth/role_grant_offer_schema.d.ts +150 -0
  125. package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
  126. package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +55 -36
  127. package/dist/auth/role_grant_queries.d.ts +231 -0
  128. package/dist/auth/role_grant_queries.d.ts.map +1 -0
  129. package/dist/auth/role_grant_queries.js +320 -0
  130. package/dist/auth/role_schema.d.ts +150 -40
  131. package/dist/auth/role_schema.d.ts.map +1 -1
  132. package/dist/auth/role_schema.js +144 -45
  133. package/dist/auth/scope_kind_schema.d.ts +96 -0
  134. package/dist/auth/scope_kind_schema.d.ts.map +1 -0
  135. package/dist/auth/scope_kind_schema.js +94 -0
  136. package/dist/auth/self_service_role_action_specs.d.ts +4 -1
  137. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
  138. package/dist/auth/self_service_role_action_specs.js +2 -2
  139. package/dist/auth/self_service_role_actions.d.ts +35 -29
  140. package/dist/auth/self_service_role_actions.d.ts.map +1 -1
  141. package/dist/auth/self_service_role_actions.js +58 -48
  142. package/dist/auth/session_cookie.d.ts +43 -6
  143. package/dist/auth/session_cookie.d.ts.map +1 -1
  144. package/dist/auth/session_cookie.js +31 -5
  145. package/dist/auth/session_middleware.d.ts +37 -3
  146. package/dist/auth/session_middleware.d.ts.map +1 -1
  147. package/dist/auth/session_middleware.js +33 -7
  148. package/dist/auth/signup_routes.d.ts.map +1 -1
  149. package/dist/auth/signup_routes.js +48 -19
  150. package/dist/auth/standard_action_specs.d.ts +2 -2
  151. package/dist/auth/standard_action_specs.js +4 -4
  152. package/dist/auth/standard_rpc_actions.d.ts +23 -19
  153. package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
  154. package/dist/auth/standard_rpc_actions.js +12 -12
  155. package/dist/db/migrate.d.ts +1 -1
  156. package/dist/db/migrate.js +1 -1
  157. package/dist/dev/setup.d.ts +2 -2
  158. package/dist/dev/setup.d.ts.map +1 -1
  159. package/dist/dev/setup.js +4 -4
  160. package/dist/env/load.d.ts +1 -1
  161. package/dist/env/load.js +1 -1
  162. package/dist/hono_context.d.ts +27 -45
  163. package/dist/hono_context.d.ts.map +1 -1
  164. package/dist/hono_context.js +14 -28
  165. package/dist/http/CLAUDE.md +235 -121
  166. package/dist/http/auth_shape.d.ts +191 -0
  167. package/dist/http/auth_shape.d.ts.map +1 -0
  168. package/dist/http/auth_shape.js +237 -0
  169. package/dist/http/common_routes.js +3 -3
  170. package/dist/http/db_routes.d.ts +4 -0
  171. package/dist/http/db_routes.d.ts.map +1 -1
  172. package/dist/http/db_routes.js +44 -7
  173. package/dist/http/error_schemas.d.ts +72 -39
  174. package/dist/http/error_schemas.d.ts.map +1 -1
  175. package/dist/http/error_schemas.js +81 -33
  176. package/dist/http/pending_effects.d.ts +71 -18
  177. package/dist/http/pending_effects.d.ts.map +1 -1
  178. package/dist/http/pending_effects.js +87 -18
  179. package/dist/http/proxy.d.ts +52 -5
  180. package/dist/http/proxy.d.ts.map +1 -1
  181. package/dist/http/proxy.js +92 -14
  182. package/dist/http/route_spec.d.ts +89 -75
  183. package/dist/http/route_spec.d.ts.map +1 -1
  184. package/dist/http/route_spec.js +54 -72
  185. package/dist/http/schema_helpers.d.ts +3 -14
  186. package/dist/http/schema_helpers.d.ts.map +1 -1
  187. package/dist/http/schema_helpers.js +2 -14
  188. package/dist/http/surface.d.ts +2 -10
  189. package/dist/http/surface.d.ts.map +1 -1
  190. package/dist/http/surface.js +3 -4
  191. package/dist/http/surface_query.d.ts +39 -35
  192. package/dist/http/surface_query.d.ts.map +1 -1
  193. package/dist/http/surface_query.js +79 -36
  194. package/dist/primitive_schemas.d.ts +39 -0
  195. package/dist/primitive_schemas.d.ts.map +1 -0
  196. package/dist/primitive_schemas.js +40 -0
  197. package/dist/realtime/sse_auth_guard.d.ts +5 -5
  198. package/dist/realtime/sse_auth_guard.js +9 -9
  199. package/dist/runtime/mock.d.ts +1 -1
  200. package/dist/runtime/mock.js +1 -1
  201. package/dist/server/app_backend.d.ts +14 -11
  202. package/dist/server/app_backend.d.ts.map +1 -1
  203. package/dist/server/app_backend.js +12 -8
  204. package/dist/server/app_server.d.ts +7 -7
  205. package/dist/server/app_server.d.ts.map +1 -1
  206. package/dist/server/app_server.js +35 -40
  207. package/dist/server/validate_nginx.d.ts +1 -1
  208. package/dist/server/validate_nginx.js +1 -1
  209. package/dist/testing/CLAUDE.md +50 -38
  210. package/dist/testing/admin_integration.d.ts +5 -6
  211. package/dist/testing/admin_integration.d.ts.map +1 -1
  212. package/dist/testing/admin_integration.js +87 -85
  213. package/dist/testing/app_server.d.ts +11 -14
  214. package/dist/testing/app_server.d.ts.map +1 -1
  215. package/dist/testing/app_server.js +16 -15
  216. package/dist/testing/assertions.d.ts.map +1 -1
  217. package/dist/testing/assertions.js +2 -1
  218. package/dist/testing/attack_surface.d.ts.map +1 -1
  219. package/dist/testing/attack_surface.js +15 -9
  220. package/dist/testing/audit_completeness.d.ts +2 -2
  221. package/dist/testing/audit_completeness.d.ts.map +1 -1
  222. package/dist/testing/audit_completeness.js +36 -36
  223. package/dist/testing/auth_apps.d.ts +5 -4
  224. package/dist/testing/auth_apps.d.ts.map +1 -1
  225. package/dist/testing/auth_apps.js +22 -19
  226. package/dist/testing/data_exposure.d.ts.map +1 -1
  227. package/dist/testing/data_exposure.js +5 -5
  228. package/dist/testing/db.d.ts +1 -1
  229. package/dist/testing/db.d.ts.map +1 -1
  230. package/dist/testing/db.js +4 -4
  231. package/dist/testing/db_entities.d.ts +22 -0
  232. package/dist/testing/db_entities.d.ts.map +1 -0
  233. package/dist/testing/db_entities.js +28 -0
  234. package/dist/testing/entities.d.ts +8 -7
  235. package/dist/testing/entities.d.ts.map +1 -1
  236. package/dist/testing/entities.js +21 -18
  237. package/dist/testing/integration.d.ts.map +1 -1
  238. package/dist/testing/integration.js +13 -14
  239. package/dist/testing/integration_helpers.d.ts +4 -4
  240. package/dist/testing/integration_helpers.d.ts.map +1 -1
  241. package/dist/testing/integration_helpers.js +20 -18
  242. package/dist/testing/middleware.d.ts +4 -4
  243. package/dist/testing/middleware.d.ts.map +1 -1
  244. package/dist/testing/middleware.js +12 -11
  245. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  246. package/dist/testing/rpc_attack_surface.js +40 -24
  247. package/dist/testing/rpc_round_trip.d.ts +1 -1
  248. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  249. package/dist/testing/rpc_round_trip.js +14 -13
  250. package/dist/testing/sse_round_trip.d.ts +3 -4
  251. package/dist/testing/sse_round_trip.d.ts.map +1 -1
  252. package/dist/testing/sse_round_trip.js +7 -11
  253. package/dist/testing/standard.d.ts +1 -1
  254. package/dist/testing/stubs.d.ts +25 -0
  255. package/dist/testing/stubs.d.ts.map +1 -1
  256. package/dist/testing/stubs.js +43 -2
  257. package/dist/testing/surface_invariants.d.ts +14 -6
  258. package/dist/testing/surface_invariants.d.ts.map +1 -1
  259. package/dist/testing/surface_invariants.js +119 -43
  260. package/dist/testing/ws_round_trip.d.ts +12 -13
  261. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  262. package/dist/testing/ws_round_trip.js +19 -11
  263. package/dist/ui/AdminAccounts.svelte +23 -20
  264. package/dist/ui/AdminOverview.svelte +15 -13
  265. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
  266. package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
  267. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
  268. package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
  269. package/dist/ui/BootstrapForm.svelte +1 -1
  270. package/dist/ui/CLAUDE.md +60 -60
  271. package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +27 -26
  272. package/dist/ui/{PermitOfferForm.svelte.d.ts → RoleGrantOfferForm.svelte.d.ts} +7 -7
  273. package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
  274. package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
  275. package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
  276. package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
  277. package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
  278. package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
  279. package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
  280. package/dist/ui/SignupForm.svelte +1 -1
  281. package/dist/ui/SurfaceExplorer.svelte +35 -15
  282. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
  283. package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
  284. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  285. package/dist/ui/account_sessions_state.svelte.js +2 -3
  286. package/dist/ui/admin_accounts_state.svelte.d.ts +18 -18
  287. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  288. package/dist/ui/admin_accounts_state.svelte.js +16 -16
  289. package/dist/ui/admin_rpc_adapters.d.ts +20 -20
  290. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
  291. package/dist/ui/admin_rpc_adapters.js +17 -17
  292. package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
  293. package/dist/ui/admin_sessions_state.svelte.js +2 -2
  294. package/dist/ui/audit_log_state.svelte.d.ts +7 -7
  295. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  296. package/dist/ui/audit_log_state.svelte.js +6 -6
  297. package/dist/ui/auth_state.svelte.d.ts +3 -3
  298. package/dist/ui/auth_state.svelte.d.ts.map +1 -1
  299. package/dist/ui/auth_state.svelte.js +6 -6
  300. package/dist/ui/format_scope.d.ts +2 -2
  301. package/dist/ui/format_scope.js +2 -2
  302. package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +30 -30
  303. package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
  304. package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +18 -18
  305. package/dist/ui/ui_format.js +2 -2
  306. package/package.json +3 -3
  307. package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
  308. package/dist/auth/permit_offer_action_specs.js +0 -258
  309. package/dist/auth/permit_offer_actions.d.ts +0 -110
  310. package/dist/auth/permit_offer_actions.d.ts.map +0 -1
  311. package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
  312. package/dist/auth/permit_offer_notifications.js +0 -182
  313. package/dist/auth/permit_offer_queries.d.ts.map +0 -1
  314. package/dist/auth/permit_offer_schema.d.ts +0 -125
  315. package/dist/auth/permit_offer_schema.d.ts.map +0 -1
  316. package/dist/auth/permit_queries.d.ts +0 -222
  317. package/dist/auth/permit_queries.d.ts.map +0 -1
  318. package/dist/auth/permit_queries.js +0 -305
  319. package/dist/auth/require_keeper.d.ts +0 -20
  320. package/dist/auth/require_keeper.d.ts.map +0 -1
  321. package/dist/auth/require_keeper.js +0 -35
  322. package/dist/auth/route_guards.d.ts +0 -27
  323. package/dist/auth/route_guards.d.ts.map +0 -1
  324. package/dist/auth/route_guards.js +0 -38
  325. package/dist/auth/session_lifecycle.d.ts +0 -37
  326. package/dist/auth/session_lifecycle.d.ts.map +0 -1
  327. package/dist/auth/session_lifecycle.js +0 -29
  328. package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
  329. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
  330. package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
  331. package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
  332. package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
  333. package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Canonical four-axis auth shape for action specs and route specs.
3
+ *
4
+ * Replaces the discriminated `'public' | 'authenticated' | 'keeper' | {role}`
5
+ * literal that conflated authentication, account resolution, actor resolution,
6
+ * and authorization into a single value. The flat record names each axis
7
+ * the dispatcher actually walks:
8
+ *
9
+ * - `account` — does the dispatcher require / load / skip the account?
10
+ * - `actor` — does the dispatcher require / load / skip the acting actor?
11
+ * - `roles` — disjunction of permitted roles (any-of); absent = no role check.
12
+ * - `credential_types` — restricts the credential channel (e.g. daemon_token);
13
+ * absent = any authenticated credential.
14
+ *
15
+ * The same shape governs both `ActionSpec.auth` (in `actions/action_spec.ts`)
16
+ * and `RouteSpec.auth` (in `http/route_spec.ts`). The canonical schema
17
+ * lives here in `http/` because that preserves the existing
18
+ * `actions → http` dependency direction (and `error_schemas.ts` /
19
+ * `surface.ts` consume the type).
20
+ *
21
+ * Registry-time invariants 1, 3, and 4 live on the schema's
22
+ * `.superRefine` so any spec that fails them throws at the Zod parse
23
+ * boundary. Invariant 2 (the `actor !== 'none' ⟺ input or query
24
+ * declares acting?: ActingActor` biconditional) needs introspection of
25
+ * the spec's input/query schemas, so it is enforced at registration
26
+ * time inside the dispatcher loops (`apply_route_specs`,
27
+ * `create_rpc_endpoint`, `register_action_ws`) via the
28
+ * `assert_route_auth_acting_biconditional` helper exported below.
29
+ *
30
+ * @module
31
+ */
32
+ import { z } from 'zod';
33
+ /**
34
+ * `acting` field shared by every input that needs the caller's acting actor.
35
+ * Declaring `acting: ActingActor` on a route or action input signals to the
36
+ * dispatcher's authorization phase to resolve an actor against the
37
+ * authenticated account: it runs `resolve_acting_actor`, builds the
38
+ * actor-bound `RequestContext`, and loads role_grants before auth guards fire.
39
+ *
40
+ * Resolution rules: omitted + 1 actor → use it; omitted + multiple actors →
41
+ * `actor_required` with the available list; supplied + on the account → use
42
+ * it; supplied + foreign actor → `actor_not_on_account`.
43
+ *
44
+ * Account-grain routes — input doesn't declare `acting` and auth doesn't
45
+ * require role_grants — skip resolution entirely; their `RequestContext.actor`
46
+ * is `null` and the audit envelope's `actor_id` stays null.
47
+ *
48
+ * Lives next to `RouteAuth` because the two are paired by registry-time
49
+ * invariant 2: `auth.actor !== 'none'` ⟺ input (or query, on REST GETs)
50
+ * declares `acting?: ActingActor`. Keeping the contract in one module
51
+ * removes the http/ → auth/ import that an earlier split forced.
52
+ */
53
+ export declare const ActingActor: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
54
+ export type ActingActor = z.infer<typeof ActingActor>;
55
+ /**
56
+ * Per-axis auth state — names the dispatcher's behavior on `account` and
57
+ * `actor` independently:
58
+ *
59
+ * - `'none'` — explicitly skipped, even when the credential provides it.
60
+ * Public actions (no auth surface) and notifications declare this.
61
+ * - `'optional'` — surfaced if the credential provides it, null otherwise.
62
+ * Identity-aware reads with anonymous fallback (cell_get-style) declare
63
+ * this on `account` / `actor`.
64
+ * - `'required'` — must be resolved; the dispatcher rejects requests that
65
+ * fail to provide it (401 for `account === 'required'` without a
66
+ * credential; the authorization phase 4xx for `actor === 'required'`
67
+ * without an actor binding).
68
+ */
69
+ export declare const AuthAxisState: z.ZodEnum<{
70
+ optional: "optional";
71
+ none: "none";
72
+ required: "required";
73
+ }>;
74
+ export type AuthAxisState = z.infer<typeof AuthAxisState>;
75
+ /**
76
+ * The canonical four-axis auth shape used by both `ActionSpec.auth` and
77
+ * `RouteSpec.auth`.
78
+ *
79
+ * Cross-axis registry invariants enforced via `.superRefine`:
80
+ *
81
+ * 1. **Roles imply actor.** `roles?.length` ⟹ `actor === 'required'`.
82
+ * Role checks read the actor's role_grants, so a role-gated spec without
83
+ * a resolved actor would have nothing to check.
84
+ * 3. **No accountless actors yet.** `account === 'none' && actor !== 'none'`
85
+ * is invalid in v1. The credential resolver always binds account before
86
+ * actor today; agent-token / group-actor credentials will lift this.
87
+ * 4. **Unrestricted is leaf.** `account === 'none' && actor === 'none'`
88
+ * ⟹ no `roles`, no `credential_types` (nothing left to gate).
89
+ *
90
+ * Invariant 2 — the `actor !== 'none' ⟺ input or query declares
91
+ * acting?: ActingActor` biconditional — needs introspection of the
92
+ * spec's input/query schemas, so it is checked at registration time,
93
+ * not on this schema. See `assert_route_auth_acting_biconditional`
94
+ * below.
95
+ */
96
+ export declare const RouteAuth: z.ZodObject<{
97
+ account: z.ZodEnum<{
98
+ optional: "optional";
99
+ none: "none";
100
+ required: "required";
101
+ }>;
102
+ actor: z.ZodEnum<{
103
+ optional: "optional";
104
+ none: "none";
105
+ required: "required";
106
+ }>;
107
+ roles: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
108
+ credential_types: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
109
+ }, z.core.$strict>;
110
+ export type RouteAuth = z.infer<typeof RouteAuth>;
111
+ /**
112
+ * True iff the route is fully public — both account and actor axes
113
+ * are `'none'`. Public routes skip the dispatcher's authorization
114
+ * phase entirely (per registry-time invariant 4 they also cannot
115
+ * declare roles or credential gates).
116
+ */
117
+ export declare const is_public_auth: (auth: RouteAuth) => boolean;
118
+ /**
119
+ * True iff the route declares an actor axis (`'optional'` or
120
+ * `'required'`). Equivalent to "the dispatcher's authorization phase
121
+ * may resolve an actor for this request" — which by registry-time
122
+ * invariant 2 also means the input (or query, on REST GETs) declares
123
+ * `acting?: ActingActor`.
124
+ */
125
+ export declare const needs_actor: (auth: RouteAuth) => boolean;
126
+ /**
127
+ * True iff the route declares an account axis (`'optional'` or
128
+ * `'required'`). Per registry-time invariant 3 this is implied by
129
+ * `needs_actor(auth)` in v1 (no accountless actors yet).
130
+ */
131
+ export declare const needs_account: (auth: RouteAuth) => boolean;
132
+ /** True iff the route declares any role gate (`auth.roles?.length`). */
133
+ export declare const is_role_auth: (auth: RouteAuth) => boolean;
134
+ /** True iff the route declares any credential-type gate (`auth.credential_types?.length`). */
135
+ export declare const is_credential_gated_auth: (auth: RouteAuth) => boolean;
136
+ /**
137
+ * True iff the route is the keeper bucket — credential gate restricted
138
+ * to `daemon_token`. Keeper is the only credential gate today; if more
139
+ * land, this filter widens. Knows the `'daemon_token'` literal directly
140
+ * (the keeper composition is fuz_app's only registered credential gate).
141
+ */
142
+ export declare const is_keeper_auth: (auth: RouteAuth) => boolean;
143
+ /**
144
+ * True iff the route is plain authenticated — `account === 'required'`
145
+ * with no role gate and no credential gate. Account-grain authenticated
146
+ * routes (logout, password change, account self-service) fall here.
147
+ */
148
+ export declare const is_plain_authenticated_auth: (auth: RouteAuth) => boolean;
149
+ /**
150
+ * Whether a schema declares the canonical `acting?: ActingActor` field.
151
+ * Reference-equality on the exported `ActingActor` schema — consumer
152
+ * schemas with unrelated `acting` fields don't trip this check.
153
+ *
154
+ * Peels through Zod wrappers (`optional`, `nullable`, `default`,
155
+ * `transform`, `pipe`, `prefault`) via `zod_unwrap_to_object` so a spec
156
+ * authored as `z.optional(z.strictObject({acting: ActingActor}))` or
157
+ * `z.strictObject({acting: ActingActor}).default({})` still trips the
158
+ * predicate.
159
+ */
160
+ export declare const input_schema_declares_acting: (schema: z.ZodType) => boolean;
161
+ /**
162
+ * Slots where a spec may declare the `acting?: ActingActor` field —
163
+ * input for both REST + actions; query for REST GETs that bi-locate
164
+ * `acting` on the query schema (actions have no `query` shape, so the
165
+ * field is omitted on action call sites).
166
+ */
167
+ export interface ActingSlots {
168
+ input: z.ZodType;
169
+ query?: z.ZodType;
170
+ }
171
+ /**
172
+ * Registry-time biconditional check: `auth.actor !== 'none' ⟺ some
173
+ * supplied slot declares acting?: ActingActor`. Throws on violation.
174
+ *
175
+ * The slot set differs by surface: REST passes `{input, query}` (both
176
+ * locatable, query only set for GETs); action dispatchers pass `{input}`
177
+ * (no query shape on `ActionSpec`). The throw message lists the slots
178
+ * that were actually in play, so an actor-required action without
179
+ * `acting` doesn't point the operator at a query slot that doesn't
180
+ * exist on their spec.
181
+ *
182
+ * Called by every dispatcher registration loop (`apply_route_specs`,
183
+ * `compile_action_registry`) on every spec it accepts.
184
+ *
185
+ * @param auth - the route/action's auth shape
186
+ * @param slots - the spec's `acting`-bearing schemas; `query` omitted on action call sites
187
+ * @param context - identifier for the throwing message (route key, RPC method, etc.)
188
+ * @throws Error when the biconditional is violated
189
+ */
190
+ export declare const assert_route_auth_acting_biconditional: (auth: RouteAuth, slots: ActingSlots, context: string) => void;
191
+ //# sourceMappingURL=auth_shape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/auth_shape.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAItB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,WAAW,6DAGtB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa;;;;EAA2C,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;kBA6CnB,CAAC;AACJ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAUlD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACA,CAAC;AAElD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,SAAS,KAAG,OAAgC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,KAAG,OAAkC,CAAC;AAEnF,wEAAwE;AACxE,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,KAAG,OAA+B,CAAC;AAE/E,8FAA8F;AAC9F,eAAO,MAAM,wBAAwB,GAAI,MAAM,SAAS,KAAG,OAC3B,CAAC;AAEjC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,SAAS,KAAG,OACQ,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,SAAS,KAAG,OACwB,CAAC;AAYvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,QAAQ,CAAC,CAAC,OAAO,KAAG,OAIhE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,OAAO,WAAW,EAClB,SAAS,MAAM,KACb,IAeF,CAAC"}
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Canonical four-axis auth shape for action specs and route specs.
3
+ *
4
+ * Replaces the discriminated `'public' | 'authenticated' | 'keeper' | {role}`
5
+ * literal that conflated authentication, account resolution, actor resolution,
6
+ * and authorization into a single value. The flat record names each axis
7
+ * the dispatcher actually walks:
8
+ *
9
+ * - `account` — does the dispatcher require / load / skip the account?
10
+ * - `actor` — does the dispatcher require / load / skip the acting actor?
11
+ * - `roles` — disjunction of permitted roles (any-of); absent = no role check.
12
+ * - `credential_types` — restricts the credential channel (e.g. daemon_token);
13
+ * absent = any authenticated credential.
14
+ *
15
+ * The same shape governs both `ActionSpec.auth` (in `actions/action_spec.ts`)
16
+ * and `RouteSpec.auth` (in `http/route_spec.ts`). The canonical schema
17
+ * lives here in `http/` because that preserves the existing
18
+ * `actions → http` dependency direction (and `error_schemas.ts` /
19
+ * `surface.ts` consume the type).
20
+ *
21
+ * Registry-time invariants 1, 3, and 4 live on the schema's
22
+ * `.superRefine` so any spec that fails them throws at the Zod parse
23
+ * boundary. Invariant 2 (the `actor !== 'none' ⟺ input or query
24
+ * declares acting?: ActingActor` biconditional) needs introspection of
25
+ * the spec's input/query schemas, so it is enforced at registration
26
+ * time inside the dispatcher loops (`apply_route_specs`,
27
+ * `create_rpc_endpoint`, `register_action_ws`) via the
28
+ * `assert_route_auth_acting_biconditional` helper exported below.
29
+ *
30
+ * @module
31
+ */
32
+ import { z } from 'zod';
33
+ import { Uuid } from '@fuzdev/fuz_util/id.js';
34
+ import { zod_unwrap_to_object } from '@fuzdev/fuz_util/zod.js';
35
+ /**
36
+ * `acting` field shared by every input that needs the caller's acting actor.
37
+ * Declaring `acting: ActingActor` on a route or action input signals to the
38
+ * dispatcher's authorization phase to resolve an actor against the
39
+ * authenticated account: it runs `resolve_acting_actor`, builds the
40
+ * actor-bound `RequestContext`, and loads role_grants before auth guards fire.
41
+ *
42
+ * Resolution rules: omitted + 1 actor → use it; omitted + multiple actors →
43
+ * `actor_required` with the available list; supplied + on the account → use
44
+ * it; supplied + foreign actor → `actor_not_on_account`.
45
+ *
46
+ * Account-grain routes — input doesn't declare `acting` and auth doesn't
47
+ * require role_grants — skip resolution entirely; their `RequestContext.actor`
48
+ * is `null` and the audit envelope's `actor_id` stays null.
49
+ *
50
+ * Lives next to `RouteAuth` because the two are paired by registry-time
51
+ * invariant 2: `auth.actor !== 'none'` ⟺ input (or query, on REST GETs)
52
+ * declares `acting?: ActingActor`. Keeping the contract in one module
53
+ * removes the http/ → auth/ import that an earlier split forced.
54
+ */
55
+ export const ActingActor = Uuid.optional().meta({
56
+ description: 'Actor on the authenticated account that this request acts as. Omit on single-actor accounts; required on multi-actor.',
57
+ });
58
+ /**
59
+ * Per-axis auth state — names the dispatcher's behavior on `account` and
60
+ * `actor` independently:
61
+ *
62
+ * - `'none'` — explicitly skipped, even when the credential provides it.
63
+ * Public actions (no auth surface) and notifications declare this.
64
+ * - `'optional'` — surfaced if the credential provides it, null otherwise.
65
+ * Identity-aware reads with anonymous fallback (cell_get-style) declare
66
+ * this on `account` / `actor`.
67
+ * - `'required'` — must be resolved; the dispatcher rejects requests that
68
+ * fail to provide it (401 for `account === 'required'` without a
69
+ * credential; the authorization phase 4xx for `actor === 'required'`
70
+ * without an actor binding).
71
+ */
72
+ export const AuthAxisState = z.enum(['none', 'optional', 'required']);
73
+ /**
74
+ * The canonical four-axis auth shape used by both `ActionSpec.auth` and
75
+ * `RouteSpec.auth`.
76
+ *
77
+ * Cross-axis registry invariants enforced via `.superRefine`:
78
+ *
79
+ * 1. **Roles imply actor.** `roles?.length` ⟹ `actor === 'required'`.
80
+ * Role checks read the actor's role_grants, so a role-gated spec without
81
+ * a resolved actor would have nothing to check.
82
+ * 3. **No accountless actors yet.** `account === 'none' && actor !== 'none'`
83
+ * is invalid in v1. The credential resolver always binds account before
84
+ * actor today; agent-token / group-actor credentials will lift this.
85
+ * 4. **Unrestricted is leaf.** `account === 'none' && actor === 'none'`
86
+ * ⟹ no `roles`, no `credential_types` (nothing left to gate).
87
+ *
88
+ * Invariant 2 — the `actor !== 'none' ⟺ input or query declares
89
+ * acting?: ActingActor` biconditional — needs introspection of the
90
+ * spec's input/query schemas, so it is checked at registration time,
91
+ * not on this schema. See `assert_route_auth_acting_biconditional`
92
+ * below.
93
+ */
94
+ export const RouteAuth = z
95
+ .strictObject({
96
+ account: AuthAxisState,
97
+ actor: AuthAxisState,
98
+ roles: z.array(z.string()).readonly().optional(),
99
+ credential_types: z.array(z.string()).readonly().optional(),
100
+ })
101
+ .superRefine((value, ctx) => {
102
+ // invariant 1: roles imply actor
103
+ if (value.roles?.length && value.actor !== 'required') {
104
+ ctx.addIssue({
105
+ code: 'custom',
106
+ message: "auth.roles requires auth.actor === 'required' (role checks read the actor's role_grants)",
107
+ path: ['roles'],
108
+ });
109
+ }
110
+ // invariant 3: no accountless actors yet
111
+ if (value.account === 'none' && value.actor !== 'none') {
112
+ ctx.addIssue({
113
+ code: 'custom',
114
+ message: "auth.account === 'none' && auth.actor !== 'none' is not yet supported — accountless credentials (agent-token, group-actor) are out of scope for v1",
115
+ path: ['actor'],
116
+ });
117
+ }
118
+ // invariant 4: unrestricted is leaf
119
+ if (value.account === 'none' && value.actor === 'none') {
120
+ if (value.roles?.length) {
121
+ ctx.addIssue({
122
+ code: 'custom',
123
+ message: "unrestricted auth (account === 'none' && actor === 'none') cannot declare roles — nothing to gate",
124
+ path: ['roles'],
125
+ });
126
+ }
127
+ if (value.credential_types?.length) {
128
+ ctx.addIssue({
129
+ code: 'custom',
130
+ message: "unrestricted auth (account === 'none' && actor === 'none') cannot declare credential_types — nothing to gate",
131
+ path: ['credential_types'],
132
+ });
133
+ }
134
+ }
135
+ });
136
+ // --- Predicates over the four-axis shape ---
137
+ //
138
+ // Pure derived reads of `RouteAuth`. Live here so every consumer that
139
+ // branches on the shape (the dispatcher's authorization phase, route
140
+ // guards, `merge_error_schemas`, `surface_query`, the surface explorer
141
+ // UI, and the testing harnesses) goes through one source of truth
142
+ // instead of inlining axis comparisons that drift over time.
143
+ /**
144
+ * True iff the route is fully public — both account and actor axes
145
+ * are `'none'`. Public routes skip the dispatcher's authorization
146
+ * phase entirely (per registry-time invariant 4 they also cannot
147
+ * declare roles or credential gates).
148
+ */
149
+ export const is_public_auth = (auth) => auth.account === 'none' && auth.actor === 'none';
150
+ /**
151
+ * True iff the route declares an actor axis (`'optional'` or
152
+ * `'required'`). Equivalent to "the dispatcher's authorization phase
153
+ * may resolve an actor for this request" — which by registry-time
154
+ * invariant 2 also means the input (or query, on REST GETs) declares
155
+ * `acting?: ActingActor`.
156
+ */
157
+ export const needs_actor = (auth) => auth.actor !== 'none';
158
+ /**
159
+ * True iff the route declares an account axis (`'optional'` or
160
+ * `'required'`). Per registry-time invariant 3 this is implied by
161
+ * `needs_actor(auth)` in v1 (no accountless actors yet).
162
+ */
163
+ export const needs_account = (auth) => auth.account !== 'none';
164
+ /** True iff the route declares any role gate (`auth.roles?.length`). */
165
+ export const is_role_auth = (auth) => !!auth.roles?.length;
166
+ /** True iff the route declares any credential-type gate (`auth.credential_types?.length`). */
167
+ export const is_credential_gated_auth = (auth) => !!auth.credential_types?.length;
168
+ /**
169
+ * True iff the route is the keeper bucket — credential gate restricted
170
+ * to `daemon_token`. Keeper is the only credential gate today; if more
171
+ * land, this filter widens. Knows the `'daemon_token'` literal directly
172
+ * (the keeper composition is fuz_app's only registered credential gate).
173
+ */
174
+ export const is_keeper_auth = (auth) => auth.credential_types?.includes('daemon_token') ?? false;
175
+ /**
176
+ * True iff the route is plain authenticated — `account === 'required'`
177
+ * with no role gate and no credential gate. Account-grain authenticated
178
+ * routes (logout, password change, account self-service) fall here.
179
+ */
180
+ export const is_plain_authenticated_auth = (auth) => auth.account === 'required' && !is_role_auth(auth) && !is_credential_gated_auth(auth);
181
+ // --- Registry-time invariant 2 enforcement ---
182
+ //
183
+ // The biconditional `auth.actor !== 'none' ⟺ input (or query, on REST
184
+ // GETs) declares acting?: ActingActor`. Cannot live on the `RouteAuth`
185
+ // schema's `.superRefine` because it requires introspecting the spec's
186
+ // input/query schemas for reference equality with the canonical
187
+ // `ActingActor` schema above. Enforced at registration time by every
188
+ // dispatcher loop (`apply_route_specs`, `compile_action_registry` for
189
+ // `create_rpc_endpoint` + `register_action_ws`).
190
+ /**
191
+ * Whether a schema declares the canonical `acting?: ActingActor` field.
192
+ * Reference-equality on the exported `ActingActor` schema — consumer
193
+ * schemas with unrelated `acting` fields don't trip this check.
194
+ *
195
+ * Peels through Zod wrappers (`optional`, `nullable`, `default`,
196
+ * `transform`, `pipe`, `prefault`) via `zod_unwrap_to_object` so a spec
197
+ * authored as `z.optional(z.strictObject({acting: ActingActor}))` or
198
+ * `z.strictObject({acting: ActingActor}).default({})` still trips the
199
+ * predicate.
200
+ */
201
+ export const input_schema_declares_acting = (schema) => {
202
+ const obj = zod_unwrap_to_object(schema);
203
+ if (!obj)
204
+ return false;
205
+ return obj.shape.acting === ActingActor;
206
+ };
207
+ /**
208
+ * Registry-time biconditional check: `auth.actor !== 'none' ⟺ some
209
+ * supplied slot declares acting?: ActingActor`. Throws on violation.
210
+ *
211
+ * The slot set differs by surface: REST passes `{input, query}` (both
212
+ * locatable, query only set for GETs); action dispatchers pass `{input}`
213
+ * (no query shape on `ActionSpec`). The throw message lists the slots
214
+ * that were actually in play, so an actor-required action without
215
+ * `acting` doesn't point the operator at a query slot that doesn't
216
+ * exist on their spec.
217
+ *
218
+ * Called by every dispatcher registration loop (`apply_route_specs`,
219
+ * `compile_action_registry`) on every spec it accepts.
220
+ *
221
+ * @param auth - the route/action's auth shape
222
+ * @param slots - the spec's `acting`-bearing schemas; `query` omitted on action call sites
223
+ * @param context - identifier for the throwing message (route key, RPC method, etc.)
224
+ * @throws Error when the biconditional is violated
225
+ */
226
+ export const assert_route_auth_acting_biconditional = (auth, slots, context) => {
227
+ const wants_actor = needs_actor(auth);
228
+ const declares_acting = input_schema_declares_acting(slots.input) ||
229
+ (slots.query !== undefined && input_schema_declares_acting(slots.query));
230
+ if (wants_actor === declares_acting)
231
+ return;
232
+ const slot_phrase = slots.query !== undefined ? 'input or query schema' : 'input schema';
233
+ if (wants_actor) {
234
+ throw new Error(`${context}: auth.actor === '${auth.actor}' requires the ${slot_phrase} to declare 'acting?: ActingActor' (registry-time invariant 2)`);
235
+ }
236
+ throw new Error(`${context}: ${slot_phrase} declares 'acting?: ActingActor' but auth.actor === 'none' (registry-time invariant 2)`);
237
+ };
@@ -16,7 +16,7 @@ import { z } from 'zod';
16
16
  export const create_health_route_spec = () => ({
17
17
  method: 'GET',
18
18
  path: '/health',
19
- auth: { type: 'none' },
19
+ auth: { account: 'none', actor: 'none' },
20
20
  handler: (c) => c.json({ status: 'ok' }),
21
21
  description: 'Health check',
22
22
  input: z.null(),
@@ -31,7 +31,7 @@ export const create_health_route_spec = () => ({
31
31
  export const create_server_status_route_spec = (options) => ({
32
32
  method: 'GET',
33
33
  path: '/api/server/status',
34
- auth: { type: 'authenticated' },
34
+ auth: { account: 'required', actor: 'none' },
35
35
  handler: (c) => c.json({ version: options.version, uptime_ms: options.get_uptime_ms() }),
36
36
  description: 'Server version and uptime',
37
37
  input: z.null(),
@@ -46,7 +46,7 @@ export const create_server_status_route_spec = (options) => ({
46
46
  export const create_surface_route_spec = (options) => ({
47
47
  method: 'GET',
48
48
  path: '/api/surface',
49
- auth: { type: 'authenticated' },
49
+ auth: { account: 'required', actor: 'none' },
50
50
  handler: (c) => c.json(options.surface),
51
51
  description: 'Application surface (routes, middleware, schemas)',
52
52
  input: z.null(),
@@ -36,6 +36,10 @@ export interface ColumnInfo {
36
36
  data_type: string;
37
37
  is_nullable: string;
38
38
  }
39
+ /** Default page size for `GET /tables/:name` rows. */
40
+ export declare const DB_TABLE_ROWS_DEFAULT_LIMIT = 100;
41
+ /** Maximum page size for `GET /tables/:name` rows. */
42
+ export declare const DB_TABLE_ROWS_LIMIT_MAX = 1000;
39
43
  /**
40
44
  * Per-factory configuration for db routes.
41
45
  */
@@ -1 +1 @@
1
- {"version":3,"file":"db_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/db_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAmB,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAYjE;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,cAAc,KAAG,KAAK,CAAC,SAAS,CA8N9E,CAAC"}
1
+ {"version":3,"file":"db_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/db_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAoC,KAAK,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAalF;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,sDAAsD;AACtD,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,sDAAsD;AACtD,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,+EAA+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,cAAc,KAAG,KAAK,CAAC,SAAS,CA0P9E,CAAC"}
@@ -7,9 +7,14 @@
7
7
  * @module
8
8
  */
9
9
  import { z } from 'zod';
10
- import { get_route_params } from './route_spec.js';
10
+ import { get_route_params, get_route_query } from './route_spec.js';
11
+ import { ActingActor } from './auth_shape.js';
11
12
  import { ForeignKeyError, ERROR_TABLE_NOT_FOUND, ERROR_TABLE_NO_PRIMARY_KEY, ERROR_ROW_NOT_FOUND, ERROR_FOREIGN_KEY_VIOLATION, ERROR_INVALID_ROUTE_PARAMS, ERROR_DATABASE_CONNECTION_FAILED, } from './error_schemas.js';
12
13
  import { assert_valid_sql_identifier, VALID_SQL_IDENTIFIER } from '../db/sql_identifier.js';
14
+ /** Default page size for `GET /tables/:name` rows. */
15
+ export const DB_TABLE_ROWS_DEFAULT_LIMIT = 100;
16
+ /** Maximum page size for `GET /tables/:name` rows. */
17
+ export const DB_TABLE_ROWS_LIMIT_MAX = 1000;
13
18
  /**
14
19
  * Create the db API route specs.
15
20
  */
@@ -19,8 +24,14 @@ export const create_db_route_specs = (options) => {
19
24
  {
20
25
  method: 'GET',
21
26
  path: '/health',
22
- auth: { type: 'keeper' },
27
+ auth: {
28
+ account: 'required',
29
+ actor: 'required',
30
+ roles: ['keeper'],
31
+ credential_types: ['daemon_token'],
32
+ },
23
33
  description: 'Database health and stats',
34
+ query: z.strictObject({ acting: ActingActor }),
24
35
  input: z.null(),
25
36
  output: z.looseObject({ connected: z.boolean() }),
26
37
  errors: {
@@ -53,8 +64,14 @@ export const create_db_route_specs = (options) => {
53
64
  {
54
65
  method: 'GET',
55
66
  path: '/tables',
56
- auth: { type: 'keeper' },
67
+ auth: {
68
+ account: 'required',
69
+ actor: 'required',
70
+ roles: ['keeper'],
71
+ credential_types: ['daemon_token'],
72
+ },
57
73
  description: 'List public tables with row counts',
74
+ query: z.strictObject({ acting: ActingActor }),
58
75
  input: z.null(),
59
76
  output: z.looseObject({
60
77
  tables: z.array(z.strictObject({ name: z.string(), row_count: z.number() })),
@@ -77,9 +94,24 @@ export const create_db_route_specs = (options) => {
77
94
  {
78
95
  method: 'GET',
79
96
  path: '/tables/:name',
80
- auth: { type: 'keeper' },
97
+ auth: {
98
+ account: 'required',
99
+ actor: 'required',
100
+ roles: ['keeper'],
101
+ credential_types: ['daemon_token'],
102
+ },
81
103
  description: 'Get table columns and rows (paginated)',
82
104
  params: z.strictObject({ name: z.string().regex(VALID_SQL_IDENTIFIER) }),
105
+ query: z.strictObject({
106
+ acting: ActingActor,
107
+ offset: z.coerce.number().int().min(0).default(0),
108
+ limit: z.coerce
109
+ .number()
110
+ .int()
111
+ .min(1)
112
+ .max(DB_TABLE_ROWS_LIMIT_MAX)
113
+ .default(DB_TABLE_ROWS_DEFAULT_LIMIT),
114
+ }),
83
115
  input: z.null(),
84
116
  errors: {
85
117
  400: z.looseObject({ error: z.literal(ERROR_INVALID_ROUTE_PARAMS) }),
@@ -95,8 +127,7 @@ export const create_db_route_specs = (options) => {
95
127
  }),
96
128
  handler: async (c, route) => {
97
129
  const { name } = get_route_params(c);
98
- const offset = Math.max(0, parseInt(c.req.query('offset') ?? '0', 10) || 0);
99
- const limit = Math.min(1000, Math.max(1, parseInt(c.req.query('limit') ?? '100', 10) || 100));
130
+ const { offset, limit } = get_route_query(c);
100
131
  const exists = await route.db.query_one(`SELECT table_name FROM information_schema.tables
101
132
  WHERE table_schema = 'public' AND table_name = $1`, [name]);
102
133
  if (!exists) {
@@ -125,12 +156,18 @@ export const create_db_route_specs = (options) => {
125
156
  {
126
157
  method: 'DELETE',
127
158
  path: '/tables/:name/rows/:id',
128
- auth: { type: 'keeper' },
159
+ auth: {
160
+ account: 'required',
161
+ actor: 'required',
162
+ roles: ['keeper'],
163
+ credential_types: ['daemon_token'],
164
+ },
129
165
  description: 'Delete a row by primary key',
130
166
  params: z.strictObject({
131
167
  name: z.string().regex(VALID_SQL_IDENTIFIER),
132
168
  id: z.string(),
133
169
  }),
170
+ query: z.strictObject({ acting: ActingActor }),
134
171
  input: z.null(),
135
172
  output: z.looseObject({ success: z.boolean() }),
136
173
  errors: {