@fuzdev/fuz_app 0.1.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 (457) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -0
  3. package/dist/actions/action_bridge.d.ts +65 -0
  4. package/dist/actions/action_bridge.d.ts.map +1 -0
  5. package/dist/actions/action_bridge.js +76 -0
  6. package/dist/actions/action_codegen.d.ts +97 -0
  7. package/dist/actions/action_codegen.d.ts.map +1 -0
  8. package/dist/actions/action_codegen.js +280 -0
  9. package/dist/actions/action_registry.d.ts +35 -0
  10. package/dist/actions/action_registry.d.ts.map +1 -0
  11. package/dist/actions/action_registry.js +83 -0
  12. package/dist/actions/action_spec.d.ts +169 -0
  13. package/dist/actions/action_spec.d.ts.map +1 -0
  14. package/dist/actions/action_spec.js +76 -0
  15. package/dist/auth/account_queries.d.ts +96 -0
  16. package/dist/auth/account_queries.d.ts.map +1 -0
  17. package/dist/auth/account_queries.js +172 -0
  18. package/dist/auth/account_routes.d.ts +86 -0
  19. package/dist/auth/account_routes.d.ts.map +1 -0
  20. package/dist/auth/account_routes.js +406 -0
  21. package/dist/auth/account_schema.d.ts +192 -0
  22. package/dist/auth/account_schema.d.ts.map +1 -0
  23. package/dist/auth/account_schema.js +105 -0
  24. package/dist/auth/admin_routes.d.ts +29 -0
  25. package/dist/auth/admin_routes.d.ts.map +1 -0
  26. package/dist/auth/admin_routes.js +193 -0
  27. package/dist/auth/api_token.d.ts +33 -0
  28. package/dist/auth/api_token.d.ts.map +1 -0
  29. package/dist/auth/api_token.js +36 -0
  30. package/dist/auth/api_token_queries.d.ts +80 -0
  31. package/dist/auth/api_token_queries.d.ts.map +1 -0
  32. package/dist/auth/api_token_queries.js +116 -0
  33. package/dist/auth/app_settings_queries.d.ts +33 -0
  34. package/dist/auth/app_settings_queries.d.ts.map +1 -0
  35. package/dist/auth/app_settings_queries.js +51 -0
  36. package/dist/auth/app_settings_routes.d.ts +27 -0
  37. package/dist/auth/app_settings_routes.d.ts.map +1 -0
  38. package/dist/auth/app_settings_routes.js +66 -0
  39. package/dist/auth/app_settings_schema.d.ts +35 -0
  40. package/dist/auth/app_settings_schema.d.ts.map +1 -0
  41. package/dist/auth/app_settings_schema.js +22 -0
  42. package/dist/auth/audit_log_queries.d.ts +90 -0
  43. package/dist/auth/audit_log_queries.d.ts.map +1 -0
  44. package/dist/auth/audit_log_queries.js +205 -0
  45. package/dist/auth/audit_log_routes.d.ts +33 -0
  46. package/dist/auth/audit_log_routes.d.ts.map +1 -0
  47. package/dist/auth/audit_log_routes.js +106 -0
  48. package/dist/auth/audit_log_schema.d.ts +259 -0
  49. package/dist/auth/audit_log_schema.d.ts.map +1 -0
  50. package/dist/auth/audit_log_schema.js +123 -0
  51. package/dist/auth/bearer_auth.d.ts +32 -0
  52. package/dist/auth/bearer_auth.d.ts.map +1 -0
  53. package/dist/auth/bearer_auth.js +90 -0
  54. package/dist/auth/bootstrap_account.d.ts +82 -0
  55. package/dist/auth/bootstrap_account.d.ts.map +1 -0
  56. package/dist/auth/bootstrap_account.js +97 -0
  57. package/dist/auth/bootstrap_routes.d.ts +74 -0
  58. package/dist/auth/bootstrap_routes.d.ts.map +1 -0
  59. package/dist/auth/bootstrap_routes.js +154 -0
  60. package/dist/auth/daemon_token.d.ts +49 -0
  61. package/dist/auth/daemon_token.d.ts.map +1 -0
  62. package/dist/auth/daemon_token.js +49 -0
  63. package/dist/auth/daemon_token_middleware.d.ts +93 -0
  64. package/dist/auth/daemon_token_middleware.d.ts.map +1 -0
  65. package/dist/auth/daemon_token_middleware.js +167 -0
  66. package/dist/auth/ddl.d.ts +27 -0
  67. package/dist/auth/ddl.d.ts.map +1 -0
  68. package/dist/auth/ddl.js +111 -0
  69. package/dist/auth/deps.d.ts +52 -0
  70. package/dist/auth/deps.d.ts.map +1 -0
  71. package/dist/auth/deps.js +10 -0
  72. package/dist/auth/invite_queries.d.ts +68 -0
  73. package/dist/auth/invite_queries.d.ts.map +1 -0
  74. package/dist/auth/invite_queries.js +105 -0
  75. package/dist/auth/invite_routes.d.ts +18 -0
  76. package/dist/auth/invite_routes.d.ts.map +1 -0
  77. package/dist/auth/invite_routes.js +129 -0
  78. package/dist/auth/invite_schema.d.ts +51 -0
  79. package/dist/auth/invite_schema.d.ts.map +1 -0
  80. package/dist/auth/invite_schema.js +25 -0
  81. package/dist/auth/keyring.d.ts +87 -0
  82. package/dist/auth/keyring.d.ts.map +1 -0
  83. package/dist/auth/keyring.js +142 -0
  84. package/dist/auth/middleware.d.ts +40 -0
  85. package/dist/auth/middleware.d.ts.map +1 -0
  86. package/dist/auth/middleware.js +64 -0
  87. package/dist/auth/migrations.d.ts +42 -0
  88. package/dist/auth/migrations.d.ts.map +1 -0
  89. package/dist/auth/migrations.js +79 -0
  90. package/dist/auth/password.d.ts +39 -0
  91. package/dist/auth/password.d.ts.map +1 -0
  92. package/dist/auth/password.js +25 -0
  93. package/dist/auth/password_argon2.d.ts +43 -0
  94. package/dist/auth/password_argon2.d.ts.map +1 -0
  95. package/dist/auth/password_argon2.js +76 -0
  96. package/dist/auth/permit_queries.d.ts +72 -0
  97. package/dist/auth/permit_queries.d.ts.map +1 -0
  98. package/dist/auth/permit_queries.js +116 -0
  99. package/dist/auth/request_context.d.ts +114 -0
  100. package/dist/auth/request_context.d.ts.map +1 -0
  101. package/dist/auth/request_context.js +176 -0
  102. package/dist/auth/require_keeper.d.ts +20 -0
  103. package/dist/auth/require_keeper.d.ts.map +1 -0
  104. package/dist/auth/require_keeper.js +35 -0
  105. package/dist/auth/role_schema.d.ts +69 -0
  106. package/dist/auth/role_schema.d.ts.map +1 -0
  107. package/dist/auth/role_schema.js +70 -0
  108. package/dist/auth/route_guards.d.ts +21 -0
  109. package/dist/auth/route_guards.d.ts.map +1 -0
  110. package/dist/auth/route_guards.js +32 -0
  111. package/dist/auth/session_cookie.d.ts +158 -0
  112. package/dist/auth/session_cookie.d.ts.map +1 -0
  113. package/dist/auth/session_cookie.js +135 -0
  114. package/dist/auth/session_lifecycle.d.ts +35 -0
  115. package/dist/auth/session_lifecycle.d.ts.map +1 -0
  116. package/dist/auth/session_lifecycle.js +27 -0
  117. package/dist/auth/session_middleware.d.ts +33 -0
  118. package/dist/auth/session_middleware.d.ts.map +1 -0
  119. package/dist/auth/session_middleware.js +62 -0
  120. package/dist/auth/session_queries.d.ts +135 -0
  121. package/dist/auth/session_queries.d.ts.map +1 -0
  122. package/dist/auth/session_queries.js +186 -0
  123. package/dist/auth/signup_routes.d.ts +32 -0
  124. package/dist/auth/signup_routes.d.ts.map +1 -0
  125. package/dist/auth/signup_routes.js +150 -0
  126. package/dist/cli/args.d.ts +48 -0
  127. package/dist/cli/args.d.ts.map +1 -0
  128. package/dist/cli/args.js +76 -0
  129. package/dist/cli/config.d.ts +48 -0
  130. package/dist/cli/config.d.ts.map +1 -0
  131. package/dist/cli/config.js +77 -0
  132. package/dist/cli/daemon.d.ts +82 -0
  133. package/dist/cli/daemon.d.ts.map +1 -0
  134. package/dist/cli/daemon.js +149 -0
  135. package/dist/cli/help.d.ts +85 -0
  136. package/dist/cli/help.d.ts.map +1 -0
  137. package/dist/cli/help.js +138 -0
  138. package/dist/cli/logger.d.ts +46 -0
  139. package/dist/cli/logger.d.ts.map +1 -0
  140. package/dist/cli/logger.js +48 -0
  141. package/dist/cli/util.d.ts +36 -0
  142. package/dist/cli/util.d.ts.map +1 -0
  143. package/dist/cli/util.js +50 -0
  144. package/dist/crypto.d.ts +13 -0
  145. package/dist/crypto.d.ts.map +1 -0
  146. package/dist/crypto.js +19 -0
  147. package/dist/db/assert_row.d.ts +18 -0
  148. package/dist/db/assert_row.d.ts.map +1 -0
  149. package/dist/db/assert_row.js +24 -0
  150. package/dist/db/create_db.d.ts +38 -0
  151. package/dist/db/create_db.d.ts.map +1 -0
  152. package/dist/db/create_db.js +57 -0
  153. package/dist/db/db.d.ts +97 -0
  154. package/dist/db/db.d.ts.map +1 -0
  155. package/dist/db/db.js +76 -0
  156. package/dist/db/db_pg.d.ts +21 -0
  157. package/dist/db/db_pg.d.ts.map +1 -0
  158. package/dist/db/db_pg.js +45 -0
  159. package/dist/db/db_pglite.d.ts +21 -0
  160. package/dist/db/db_pglite.d.ts.map +1 -0
  161. package/dist/db/db_pglite.js +28 -0
  162. package/dist/db/migrate.d.ts +67 -0
  163. package/dist/db/migrate.d.ts.map +1 -0
  164. package/dist/db/migrate.js +118 -0
  165. package/dist/db/pg_error.d.ts +16 -0
  166. package/dist/db/pg_error.d.ts.map +1 -0
  167. package/dist/db/pg_error.js +15 -0
  168. package/dist/db/query_deps.d.ts +14 -0
  169. package/dist/db/query_deps.d.ts.map +1 -0
  170. package/dist/db/query_deps.js +9 -0
  171. package/dist/db/sql_identifier.d.ts +27 -0
  172. package/dist/db/sql_identifier.d.ts.map +1 -0
  173. package/dist/db/sql_identifier.js +31 -0
  174. package/dist/db/status.d.ts +62 -0
  175. package/dist/db/status.d.ts.map +1 -0
  176. package/dist/db/status.js +116 -0
  177. package/dist/dev/setup.d.ts +159 -0
  178. package/dist/dev/setup.d.ts.map +1 -0
  179. package/dist/dev/setup.js +265 -0
  180. package/dist/env/dotenv.d.ts +25 -0
  181. package/dist/env/dotenv.d.ts.map +1 -0
  182. package/dist/env/dotenv.js +52 -0
  183. package/dist/env/load.d.ts +52 -0
  184. package/dist/env/load.d.ts.map +1 -0
  185. package/dist/env/load.js +79 -0
  186. package/dist/env/mask.d.ts +19 -0
  187. package/dist/env/mask.d.ts.map +1 -0
  188. package/dist/env/mask.js +26 -0
  189. package/dist/env/resolve.d.ts +126 -0
  190. package/dist/env/resolve.d.ts.map +1 -0
  191. package/dist/env/resolve.js +200 -0
  192. package/dist/hono_context.d.ts +48 -0
  193. package/dist/hono_context.d.ts.map +1 -0
  194. package/dist/hono_context.js +22 -0
  195. package/dist/http/common_routes.d.ts +52 -0
  196. package/dist/http/common_routes.d.ts.map +1 -0
  197. package/dist/http/common_routes.js +65 -0
  198. package/dist/http/db_routes.d.ts +57 -0
  199. package/dist/http/db_routes.d.ts.map +1 -0
  200. package/dist/http/db_routes.js +176 -0
  201. package/dist/http/error_schemas.d.ts +169 -0
  202. package/dist/http/error_schemas.d.ts.map +1 -0
  203. package/dist/http/error_schemas.js +178 -0
  204. package/dist/http/middleware_spec.d.ts +19 -0
  205. package/dist/http/middleware_spec.d.ts.map +1 -0
  206. package/dist/http/middleware_spec.js +9 -0
  207. package/dist/http/origin.d.ts +57 -0
  208. package/dist/http/origin.d.ts.map +1 -0
  209. package/dist/http/origin.js +207 -0
  210. package/dist/http/proxy.d.ts +112 -0
  211. package/dist/http/proxy.d.ts.map +1 -0
  212. package/dist/http/proxy.js +240 -0
  213. package/dist/http/route_spec.d.ts +197 -0
  214. package/dist/http/route_spec.d.ts.map +1 -0
  215. package/dist/http/route_spec.js +243 -0
  216. package/dist/http/schema_helpers.d.ts +64 -0
  217. package/dist/http/schema_helpers.d.ts.map +1 -0
  218. package/dist/http/schema_helpers.js +90 -0
  219. package/dist/http/surface.d.ts +132 -0
  220. package/dist/http/surface.d.ts.map +1 -0
  221. package/dist/http/surface.js +156 -0
  222. package/dist/http/surface_query.d.ts +77 -0
  223. package/dist/http/surface_query.d.ts.map +1 -0
  224. package/dist/http/surface_query.js +86 -0
  225. package/dist/rate_limiter.d.ts +94 -0
  226. package/dist/rate_limiter.d.ts.map +1 -0
  227. package/dist/rate_limiter.js +156 -0
  228. package/dist/realtime/sse.d.ts +80 -0
  229. package/dist/realtime/sse.d.ts.map +1 -0
  230. package/dist/realtime/sse.js +109 -0
  231. package/dist/realtime/sse_auth_guard.d.ts +93 -0
  232. package/dist/realtime/sse_auth_guard.d.ts.map +1 -0
  233. package/dist/realtime/sse_auth_guard.js +111 -0
  234. package/dist/realtime/subscriber_registry.d.ts +85 -0
  235. package/dist/realtime/subscriber_registry.d.ts.map +1 -0
  236. package/dist/realtime/subscriber_registry.js +108 -0
  237. package/dist/runtime/deno.d.ts +21 -0
  238. package/dist/runtime/deno.d.ts.map +1 -0
  239. package/dist/runtime/deno.js +83 -0
  240. package/dist/runtime/deps.d.ts +113 -0
  241. package/dist/runtime/deps.d.ts.map +1 -0
  242. package/dist/runtime/deps.js +10 -0
  243. package/dist/runtime/fs.d.ts +15 -0
  244. package/dist/runtime/fs.d.ts.map +1 -0
  245. package/dist/runtime/fs.js +17 -0
  246. package/dist/runtime/mock.d.ts +81 -0
  247. package/dist/runtime/mock.d.ts.map +1 -0
  248. package/dist/runtime/mock.js +195 -0
  249. package/dist/runtime/node.d.ts +17 -0
  250. package/dist/runtime/node.d.ts.map +1 -0
  251. package/dist/runtime/node.js +117 -0
  252. package/dist/schema_meta.d.ts +16 -0
  253. package/dist/schema_meta.d.ts.map +1 -0
  254. package/dist/schema_meta.js +9 -0
  255. package/dist/sensitivity.d.ts +15 -0
  256. package/dist/sensitivity.d.ts.map +1 -0
  257. package/dist/sensitivity.js +9 -0
  258. package/dist/server/app_backend.d.ts +74 -0
  259. package/dist/server/app_backend.d.ts.map +1 -0
  260. package/dist/server/app_backend.js +39 -0
  261. package/dist/server/app_server.d.ts +201 -0
  262. package/dist/server/app_server.d.ts.map +1 -0
  263. package/dist/server/app_server.js +266 -0
  264. package/dist/server/env.d.ts +68 -0
  265. package/dist/server/env.d.ts.map +1 -0
  266. package/dist/server/env.js +95 -0
  267. package/dist/server/startup.d.ts +22 -0
  268. package/dist/server/startup.d.ts.map +1 -0
  269. package/dist/server/startup.js +48 -0
  270. package/dist/server/static.d.ts +39 -0
  271. package/dist/server/static.d.ts.map +1 -0
  272. package/dist/server/static.js +38 -0
  273. package/dist/server/validate_nginx.d.ts +34 -0
  274. package/dist/server/validate_nginx.d.ts.map +1 -0
  275. package/dist/server/validate_nginx.js +118 -0
  276. package/dist/testing/CLAUDE.md +3 -0
  277. package/dist/testing/admin_integration.d.ts +45 -0
  278. package/dist/testing/admin_integration.d.ts.map +1 -0
  279. package/dist/testing/admin_integration.js +840 -0
  280. package/dist/testing/adversarial_404.d.ts +15 -0
  281. package/dist/testing/adversarial_404.d.ts.map +1 -0
  282. package/dist/testing/adversarial_404.js +118 -0
  283. package/dist/testing/adversarial_headers.d.ts +36 -0
  284. package/dist/testing/adversarial_headers.d.ts.map +1 -0
  285. package/dist/testing/adversarial_headers.js +128 -0
  286. package/dist/testing/adversarial_input.d.ts +56 -0
  287. package/dist/testing/adversarial_input.d.ts.map +1 -0
  288. package/dist/testing/adversarial_input.js +494 -0
  289. package/dist/testing/app_server.d.ts +169 -0
  290. package/dist/testing/app_server.d.ts.map +1 -0
  291. package/dist/testing/app_server.js +240 -0
  292. package/dist/testing/assert_dev_env.d.ts +10 -0
  293. package/dist/testing/assert_dev_env.d.ts.map +1 -0
  294. package/dist/testing/assert_dev_env.js +13 -0
  295. package/dist/testing/assertions.d.ts +61 -0
  296. package/dist/testing/assertions.d.ts.map +1 -0
  297. package/dist/testing/assertions.js +96 -0
  298. package/dist/testing/attack_surface.d.ts +63 -0
  299. package/dist/testing/attack_surface.d.ts.map +1 -0
  300. package/dist/testing/attack_surface.js +224 -0
  301. package/dist/testing/audit_completeness.d.ts +29 -0
  302. package/dist/testing/audit_completeness.d.ts.map +1 -0
  303. package/dist/testing/audit_completeness.js +410 -0
  304. package/dist/testing/auth_apps.d.ts +55 -0
  305. package/dist/testing/auth_apps.d.ts.map +1 -0
  306. package/dist/testing/auth_apps.js +122 -0
  307. package/dist/testing/data_exposure.d.ts +62 -0
  308. package/dist/testing/data_exposure.d.ts.map +1 -0
  309. package/dist/testing/data_exposure.js +297 -0
  310. package/dist/testing/db.d.ts +111 -0
  311. package/dist/testing/db.d.ts.map +1 -0
  312. package/dist/testing/db.js +258 -0
  313. package/dist/testing/entities.d.ts +21 -0
  314. package/dist/testing/entities.d.ts.map +1 -0
  315. package/dist/testing/entities.js +42 -0
  316. package/dist/testing/error_coverage.d.ts +78 -0
  317. package/dist/testing/error_coverage.d.ts.map +1 -0
  318. package/dist/testing/error_coverage.js +135 -0
  319. package/dist/testing/integration.d.ts +37 -0
  320. package/dist/testing/integration.d.ts.map +1 -0
  321. package/dist/testing/integration.js +1139 -0
  322. package/dist/testing/integration_helpers.d.ts +107 -0
  323. package/dist/testing/integration_helpers.d.ts.map +1 -0
  324. package/dist/testing/integration_helpers.js +246 -0
  325. package/dist/testing/middleware.d.ts +125 -0
  326. package/dist/testing/middleware.d.ts.map +1 -0
  327. package/dist/testing/middleware.js +210 -0
  328. package/dist/testing/rate_limiting.d.ts +43 -0
  329. package/dist/testing/rate_limiting.d.ts.map +1 -0
  330. package/dist/testing/rate_limiting.js +216 -0
  331. package/dist/testing/round_trip.d.ts +37 -0
  332. package/dist/testing/round_trip.d.ts.map +1 -0
  333. package/dist/testing/round_trip.js +128 -0
  334. package/dist/testing/schema_generators.d.ts +33 -0
  335. package/dist/testing/schema_generators.d.ts.map +1 -0
  336. package/dist/testing/schema_generators.js +137 -0
  337. package/dist/testing/standard.d.ts +49 -0
  338. package/dist/testing/standard.d.ts.map +1 -0
  339. package/dist/testing/standard.js +16 -0
  340. package/dist/testing/stubs.d.ts +96 -0
  341. package/dist/testing/stubs.d.ts.map +1 -0
  342. package/dist/testing/stubs.js +192 -0
  343. package/dist/testing/surface_invariants.d.ts +189 -0
  344. package/dist/testing/surface_invariants.d.ts.map +1 -0
  345. package/dist/testing/surface_invariants.js +450 -0
  346. package/dist/ui/AccountSessions.svelte +75 -0
  347. package/dist/ui/AccountSessions.svelte.d.ts +19 -0
  348. package/dist/ui/AccountSessions.svelte.d.ts.map +1 -0
  349. package/dist/ui/AdminAccounts.svelte +107 -0
  350. package/dist/ui/AdminAccounts.svelte.d.ts +19 -0
  351. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -0
  352. package/dist/ui/AdminAuditLog.svelte +144 -0
  353. package/dist/ui/AdminAuditLog.svelte.d.ts +4 -0
  354. package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -0
  355. package/dist/ui/AdminInvites.svelte +142 -0
  356. package/dist/ui/AdminInvites.svelte.d.ts +4 -0
  357. package/dist/ui/AdminInvites.svelte.d.ts.map +1 -0
  358. package/dist/ui/AdminOverview.svelte +337 -0
  359. package/dist/ui/AdminOverview.svelte.d.ts +4 -0
  360. package/dist/ui/AdminOverview.svelte.d.ts.map +1 -0
  361. package/dist/ui/AdminPermitHistory.svelte +61 -0
  362. package/dist/ui/AdminPermitHistory.svelte.d.ts +19 -0
  363. package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -0
  364. package/dist/ui/AdminSessions.svelte +85 -0
  365. package/dist/ui/AdminSessions.svelte.d.ts +19 -0
  366. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -0
  367. package/dist/ui/AdminSettings.svelte +32 -0
  368. package/dist/ui/AdminSettings.svelte.d.ts +19 -0
  369. package/dist/ui/AdminSettings.svelte.d.ts.map +1 -0
  370. package/dist/ui/AdminSurface.svelte +42 -0
  371. package/dist/ui/AdminSurface.svelte.d.ts +4 -0
  372. package/dist/ui/AdminSurface.svelte.d.ts.map +1 -0
  373. package/dist/ui/AppShell.svelte +93 -0
  374. package/dist/ui/AppShell.svelte.d.ts +20 -0
  375. package/dist/ui/AppShell.svelte.d.ts.map +1 -0
  376. package/dist/ui/BootstrapForm.svelte +105 -0
  377. package/dist/ui/BootstrapForm.svelte.d.ts +4 -0
  378. package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -0
  379. package/dist/ui/ColumnLayout.svelte +46 -0
  380. package/dist/ui/ColumnLayout.svelte.d.ts +11 -0
  381. package/dist/ui/ColumnLayout.svelte.d.ts.map +1 -0
  382. package/dist/ui/ConfirmButton.svelte +125 -0
  383. package/dist/ui/ConfirmButton.svelte.d.ts +54 -0
  384. package/dist/ui/ConfirmButton.svelte.d.ts.map +1 -0
  385. package/dist/ui/Datatable.svelte +185 -0
  386. package/dist/ui/Datatable.svelte.d.ts +35 -0
  387. package/dist/ui/Datatable.svelte.d.ts.map +1 -0
  388. package/dist/ui/LoginForm.svelte +82 -0
  389. package/dist/ui/LoginForm.svelte.d.ts +8 -0
  390. package/dist/ui/LoginForm.svelte.d.ts.map +1 -0
  391. package/dist/ui/LogoutButton.svelte +36 -0
  392. package/dist/ui/LogoutButton.svelte.d.ts +10 -0
  393. package/dist/ui/LogoutButton.svelte.d.ts.map +1 -0
  394. package/dist/ui/MenuLink.svelte +35 -0
  395. package/dist/ui/MenuLink.svelte.d.ts +12 -0
  396. package/dist/ui/MenuLink.svelte.d.ts.map +1 -0
  397. package/dist/ui/OpenSignupToggle.svelte +36 -0
  398. package/dist/ui/OpenSignupToggle.svelte.d.ts +19 -0
  399. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -0
  400. package/dist/ui/PopoverButton.svelte +136 -0
  401. package/dist/ui/PopoverButton.svelte.d.ts +63 -0
  402. package/dist/ui/PopoverButton.svelte.d.ts.map +1 -0
  403. package/dist/ui/SignupForm.svelte +117 -0
  404. package/dist/ui/SignupForm.svelte.d.ts +7 -0
  405. package/dist/ui/SignupForm.svelte.d.ts.map +1 -0
  406. package/dist/ui/SurfaceExplorer.svelte +287 -0
  407. package/dist/ui/SurfaceExplorer.svelte.d.ts +8 -0
  408. package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -0
  409. package/dist/ui/account_sessions_state.svelte.d.ts +15 -0
  410. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -0
  411. package/dist/ui/account_sessions_state.svelte.js +45 -0
  412. package/dist/ui/admin_accounts_state.svelte.d.ts +19 -0
  413. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -0
  414. package/dist/ui/admin_accounts_state.svelte.js +65 -0
  415. package/dist/ui/admin_invites_state.svelte.d.ts +19 -0
  416. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -0
  417. package/dist/ui/admin_invites_state.svelte.js +71 -0
  418. package/dist/ui/admin_sessions_state.svelte.d.ts +18 -0
  419. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -0
  420. package/dist/ui/admin_sessions_state.svelte.js +62 -0
  421. package/dist/ui/app_settings_state.svelte.d.ts +14 -0
  422. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -0
  423. package/dist/ui/app_settings_state.svelte.js +44 -0
  424. package/dist/ui/audit_log_state.svelte.d.ts +40 -0
  425. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -0
  426. package/dist/ui/audit_log_state.svelte.js +153 -0
  427. package/dist/ui/auth_state.svelte.d.ts +85 -0
  428. package/dist/ui/auth_state.svelte.d.ts.map +1 -0
  429. package/dist/ui/auth_state.svelte.js +238 -0
  430. package/dist/ui/datatable.d.ts +25 -0
  431. package/dist/ui/datatable.d.ts.map +1 -0
  432. package/dist/ui/datatable.js +9 -0
  433. package/dist/ui/enter_advance.d.ts +13 -0
  434. package/dist/ui/enter_advance.d.ts.map +1 -0
  435. package/dist/ui/enter_advance.js +30 -0
  436. package/dist/ui/loadable.svelte.d.ts +55 -0
  437. package/dist/ui/loadable.svelte.d.ts.map +1 -0
  438. package/dist/ui/loadable.svelte.js +75 -0
  439. package/dist/ui/popover.svelte.d.ts +137 -0
  440. package/dist/ui/popover.svelte.d.ts.map +1 -0
  441. package/dist/ui/popover.svelte.js +288 -0
  442. package/dist/ui/position_helpers.d.ts +27 -0
  443. package/dist/ui/position_helpers.d.ts.map +1 -0
  444. package/dist/ui/position_helpers.js +81 -0
  445. package/dist/ui/sidebar_state.svelte.d.ts +30 -0
  446. package/dist/ui/sidebar_state.svelte.d.ts.map +1 -0
  447. package/dist/ui/sidebar_state.svelte.js +39 -0
  448. package/dist/ui/table_state.svelte.d.ts +63 -0
  449. package/dist/ui/table_state.svelte.d.ts.map +1 -0
  450. package/dist/ui/table_state.svelte.js +117 -0
  451. package/dist/ui/ui_fetch.d.ts +29 -0
  452. package/dist/ui/ui_fetch.d.ts.map +1 -0
  453. package/dist/ui/ui_fetch.js +37 -0
  454. package/dist/ui/ui_format.d.ts +63 -0
  455. package/dist/ui/ui_format.d.ts.map +1 -0
  456. package/dist/ui/ui_format.js +196 -0
  457. package/package.json +121 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Audit log database schema and types.
3
+ *
4
+ * Records auth mutations (login, logout, grant, revoke, etc.) for
5
+ * security monitoring and operational visibility.
6
+ *
7
+ * @module
8
+ */
9
+ import { z } from 'zod';
10
+ import { AuthSessionJson } from './account_schema.js';
11
+ /** All tracked auth event types. */
12
+ export const AUDIT_EVENT_TYPES = [
13
+ 'login',
14
+ 'logout',
15
+ 'bootstrap',
16
+ 'signup',
17
+ 'password_change',
18
+ 'session_revoke',
19
+ 'session_revoke_all',
20
+ 'token_create',
21
+ 'token_revoke',
22
+ 'token_revoke_all',
23
+ 'permit_grant',
24
+ 'permit_revoke',
25
+ 'invite_create',
26
+ 'invite_delete',
27
+ 'app_settings_update',
28
+ ];
29
+ /** Zod schema for audit event types. */
30
+ export const AuditEventType = z.enum(AUDIT_EVENT_TYPES);
31
+ /** Zod schema for audit event outcomes. */
32
+ export const AuditOutcome = z.enum(['success', 'failure']);
33
+ /**
34
+ * Per-event-type metadata Zod schemas.
35
+ *
36
+ * Uses `z.looseObject` so consumers can add extra fields
37
+ * (e.g. visiones `self_service`) while known fields are validated.
38
+ * Events with outcome-dependent metadata use a union with `z.null()`.
39
+ */
40
+ export const AUDIT_METADATA_SCHEMAS = {
41
+ login: z.looseObject({ username: z.string() }).nullable(),
42
+ logout: z.null(),
43
+ bootstrap: z.looseObject({ error: z.string() }).nullable(),
44
+ signup: z.looseObject({
45
+ username: z.string(),
46
+ invite_id: z.string().optional(),
47
+ open_signup: z.boolean().optional(),
48
+ }),
49
+ password_change: z.looseObject({ sessions_revoked: z.number() }).nullable(),
50
+ session_revoke: z.looseObject({ session_id: z.string() }),
51
+ session_revoke_all: z.looseObject({ count: z.number() }),
52
+ token_create: z.looseObject({ token_id: z.string(), name: z.string() }),
53
+ token_revoke: z.looseObject({ token_id: z.string() }),
54
+ token_revoke_all: z.looseObject({ count: z.number() }),
55
+ permit_grant: z.looseObject({ role: z.string(), permit_id: z.string() }),
56
+ permit_revoke: z.looseObject({ role: z.string(), permit_id: z.string() }),
57
+ invite_create: z.looseObject({
58
+ invite_id: z.string(),
59
+ email: z.string().nullable(),
60
+ username: z.string().nullable(),
61
+ }),
62
+ invite_delete: z.looseObject({ invite_id: z.string() }),
63
+ app_settings_update: z.looseObject({
64
+ setting: z.string(),
65
+ old_value: z.unknown(),
66
+ new_value: z.unknown(),
67
+ }),
68
+ };
69
+ /**
70
+ * Narrow metadata type for a known event type.
71
+ *
72
+ * Use after checking `event_type` to get typed metadata access.
73
+ */
74
+ export const get_audit_metadata = (event) => {
75
+ return event.metadata;
76
+ };
77
+ /** Zod schema for client-safe audit log event. */
78
+ export const AuditLogEventJson = z.strictObject({
79
+ id: z.string(),
80
+ seq: z.number().int(),
81
+ event_type: AuditEventType,
82
+ outcome: AuditOutcome,
83
+ actor_id: z.string().nullable(),
84
+ account_id: z.string().nullable(),
85
+ target_account_id: z.string().nullable(),
86
+ ip: z.string().nullable(),
87
+ created_at: z.string(),
88
+ metadata: z.record(z.string(), z.unknown()).nullable(),
89
+ });
90
+ /** Zod schema for audit log events with resolved usernames. */
91
+ export const AuditLogEventWithUsernamesJson = AuditLogEventJson.extend({
92
+ username: z.string().nullable(),
93
+ target_username: z.string().nullable(),
94
+ });
95
+ /** Zod schema for permit history events with resolved usernames. */
96
+ export const PermitHistoryEventJson = AuditLogEventJson.extend({
97
+ username: z.string().nullable(),
98
+ target_username: z.string().nullable(),
99
+ });
100
+ /** Zod schema for admin session listing (session + username). */
101
+ export const AdminSessionJson = AuthSessionJson.extend({
102
+ username: z.string(),
103
+ });
104
+ // Schema DDL
105
+ export const AUDIT_LOG_SCHEMA = `
106
+ CREATE TABLE IF NOT EXISTS audit_log (
107
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
108
+ seq SERIAL NOT NULL,
109
+ event_type TEXT NOT NULL,
110
+ outcome TEXT NOT NULL DEFAULT 'success',
111
+ actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
112
+ account_id UUID REFERENCES account(id) ON DELETE SET NULL,
113
+ target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,
114
+ ip TEXT,
115
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
116
+ metadata JSONB
117
+ )`;
118
+ export const AUDIT_LOG_INDEXES = [
119
+ `CREATE INDEX IF NOT EXISTS idx_audit_log_seq ON audit_log(seq DESC)`,
120
+ `CREATE INDEX IF NOT EXISTS idx_audit_log_account ON audit_log(account_id)`,
121
+ `CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)`,
122
+ `CREATE INDEX IF NOT EXISTS idx_audit_log_target_account ON audit_log(target_account_id)`,
123
+ ];
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Bearer auth middleware for API token authentication.
3
+ *
4
+ * Bearer tokens are rejected when `Origin` or `Referer` headers are present —
5
+ * browsers must use cookie auth. This reduces attack surface: a stolen token
6
+ * cannot be replayed from a browser context (the browser adds `Origin`
7
+ * automatically).
8
+ *
9
+ * Token generation and hashing utilities live in `auth/api_token.ts`.
10
+ *
11
+ * @module
12
+ */
13
+ import type { MiddlewareHandler } from 'hono';
14
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
15
+ import type { QueryDeps } from '../db/query_deps.js';
16
+ import { type RateLimiter } from '../rate_limiter.js';
17
+ /**
18
+ * Create middleware that authenticates via bearer token.
19
+ *
20
+ * Rejects bearer tokens when an `Origin` or `Referer` header is present —
21
+ * browsers must use cookie auth to reduce attack surface.
22
+ * Auth scheme matching is case-insensitive per RFC 7235.
23
+ * On success, builds the request context (`{ account, actor, permits }`)
24
+ * and sets it on the Hono context. Skips if a request context is already set
25
+ * (e.g. by session middleware).
26
+ *
27
+ * @param deps - query dependencies (pool-level db for middleware)
28
+ * @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
29
+ * @param log - the logger instance
30
+ */
31
+ export declare const create_bearer_auth_middleware: (deps: QueryDeps, ip_rate_limiter: RateLimiter | null, log: Logger) => MiddlewareHandler;
32
+ //# sourceMappingURL=bearer_auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bearer_auth.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bearer_auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,MAAM,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAKpD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAOlF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,iBAAiB,WAAW,GAAG,IAAI,EACnC,KAAK,MAAM,KACT,iBAwEF,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Bearer auth middleware for API token authentication.
3
+ *
4
+ * Bearer tokens are rejected when `Origin` or `Referer` headers are present —
5
+ * browsers must use cookie auth. This reduces attack surface: a stolen token
6
+ * cannot be replayed from a browser context (the browser adds `Origin`
7
+ * automatically).
8
+ *
9
+ * Token generation and hashing utilities live in `auth/api_token.ts`.
10
+ *
11
+ * @module
12
+ */
13
+ import { REQUEST_CONTEXT_KEY, build_request_context } from './request_context.js';
14
+ import { CREDENTIAL_TYPE_KEY } from '../hono_context.js';
15
+ import { query_validate_api_token } from './api_token_queries.js';
16
+ import { get_client_ip } from '../http/proxy.js';
17
+ import { rate_limit_exceeded_response } from '../rate_limiter.js';
18
+ import { ERROR_BEARER_REJECTED_BROWSER, ERROR_INVALID_TOKEN, ERROR_ACCOUNT_NOT_FOUND, } from '../http/error_schemas.js';
19
+ /**
20
+ * Create middleware that authenticates via bearer token.
21
+ *
22
+ * Rejects bearer tokens when an `Origin` or `Referer` header is present —
23
+ * browsers must use cookie auth to reduce attack surface.
24
+ * Auth scheme matching is case-insensitive per RFC 7235.
25
+ * On success, builds the request context (`{ account, actor, permits }`)
26
+ * and sets it on the Hono context. Skips if a request context is already set
27
+ * (e.g. by session middleware).
28
+ *
29
+ * @param deps - query dependencies (pool-level db for middleware)
30
+ * @param ip_rate_limiter - per-IP rate limiter for bearer token attempts (null to disable)
31
+ * @param log - the logger instance
32
+ */
33
+ export const create_bearer_auth_middleware = (deps, ip_rate_limiter, log) => {
34
+ return async (c, next) => {
35
+ // Skip if already authenticated via session
36
+ if (c.get(REQUEST_CONTEXT_KEY)) {
37
+ await next();
38
+ return;
39
+ }
40
+ const auth_header = c.req.header('Authorization');
41
+ // Case-insensitive scheme matching per RFC 7235 §2.1 — defense-in-depth:
42
+ // without this, a `bearer` (lowercase) header silently bypasses token
43
+ // validation and browser-context rejection instead of being processed.
44
+ // eslint-disable-next-line @typescript-eslint/prefer-optional-chain
45
+ if (!auth_header || auth_header.slice(0, 7).toLowerCase() !== 'bearer ') {
46
+ await next();
47
+ return;
48
+ }
49
+ // Reject bearer tokens in browser context — defense-in-depth:
50
+ // checks both Origin and Referer (not just Origin) because some browser
51
+ // requests send only Referer. Uses `!== undefined` so that empty-string
52
+ // headers (e.g. `Origin: ''`) are still treated as browser context.
53
+ if (c.req.header('Origin') !== undefined || c.req.header('Referer') !== undefined) {
54
+ return c.json({ error: ERROR_BEARER_REJECTED_BROWSER }, 403);
55
+ }
56
+ const raw_token = auth_header.slice(7);
57
+ // Reject empty token body before any hashing or DB work.
58
+ // (The Fetch API trims 'Bearer ' to 'Bearer' which skips this middleware entirely,
59
+ // but raw HTTP clients may send 'Bearer ' with an empty token.)
60
+ if (!raw_token) {
61
+ return c.json({ error: ERROR_INVALID_TOKEN }, 401);
62
+ }
63
+ const ip = get_client_ip(c);
64
+ // Per-IP rate limit: record before async DB work to close the TOCTOU
65
+ // window where concurrent requests could all pass check() before any
66
+ // reaches record(). On valid token, reset the counter below.
67
+ if (ip_rate_limiter) {
68
+ const check = ip_rate_limiter.check(ip);
69
+ if (!check.allowed) {
70
+ return rate_limit_exceeded_response(c, check.retry_after);
71
+ }
72
+ ip_rate_limiter.record(ip);
73
+ }
74
+ const api_token = await query_validate_api_token({ ...deps, log }, raw_token, ip, c.var.pending_effects);
75
+ if (!api_token) {
76
+ return c.json({ error: ERROR_INVALID_TOKEN }, 401);
77
+ }
78
+ // Valid token — reset rate limit counter
79
+ if (ip_rate_limiter)
80
+ ip_rate_limiter.reset(ip);
81
+ // Build request context from the token's account
82
+ const ctx = await build_request_context(deps, api_token.account_id);
83
+ if (!ctx) {
84
+ return c.json({ error: ERROR_ACCOUNT_NOT_FOUND }, 401);
85
+ }
86
+ c.set(REQUEST_CONTEXT_KEY, ctx);
87
+ c.set(CREDENTIAL_TYPE_KEY, 'api_token');
88
+ await next();
89
+ };
90
+ };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Bootstrap flow for creating the first account.
3
+ *
4
+ * Uses an atomic `bootstrap_lock` table to prevent TOCTOU race conditions.
5
+ * Token verification and account creation happen in a single transaction.
6
+ *
7
+ * @module
8
+ */
9
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
10
+ import type { PasswordHashDeps } from './password.js';
11
+ import { ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING } from '../http/error_schemas.js';
12
+ import type { Account, Actor, Permit } from './account_schema.js';
13
+ import type { Db } from '../db/db.js';
14
+ /** Input for the bootstrap account creation. */
15
+ export interface BootstrapAccountInput {
16
+ username: string;
17
+ password: string;
18
+ }
19
+ /** Successful bootstrap result with the created entities. */
20
+ export interface BootstrapAccountSuccess {
21
+ ok: true;
22
+ account: Account;
23
+ actor: Actor;
24
+ permits: {
25
+ keeper: Permit;
26
+ admin: Permit;
27
+ };
28
+ /** Whether the bootstrap token file was successfully deleted after account creation. */
29
+ token_file_deleted: boolean;
30
+ }
31
+ /** Bootstrap failure result. */
32
+ export type BootstrapAccountFailure = {
33
+ ok: false;
34
+ error: typeof ERROR_ALREADY_BOOTSTRAPPED;
35
+ status: 403;
36
+ } | {
37
+ ok: false;
38
+ error: typeof ERROR_TOKEN_FILE_MISSING;
39
+ status: 404;
40
+ } | {
41
+ ok: false;
42
+ error: typeof ERROR_INVALID_TOKEN;
43
+ status: 401;
44
+ };
45
+ /** Bootstrap account result — either success or a bootstrap verification failure. */
46
+ export type BootstrapAccountResult = BootstrapAccountSuccess | BootstrapAccountFailure;
47
+ /**
48
+ * Dependencies for `bootstrap_account`.
49
+ */
50
+ export interface BootstrapAccountDeps {
51
+ db: Db;
52
+ /** Path to the bootstrap token file on disk. */
53
+ token_path: string;
54
+ /** Read a file's contents as a string. */
55
+ read_file: (path: string) => Promise<string>;
56
+ /** Delete a file. */
57
+ delete_file: (path: string) => Promise<void>;
58
+ /** Only hashing is needed — verification happens separately during login. */
59
+ password: Pick<PasswordHashDeps, 'hash_password'>;
60
+ /** Structured logger instance. */
61
+ log: Logger;
62
+ }
63
+ /**
64
+ * Bootstrap the first account with keeper and admin privileges.
65
+ *
66
+ * Uses an atomic `bootstrap_lock` UPDATE to prevent concurrent bootstrap
67
+ * attempts (TOCTOU). The full flow runs in a single transaction:
68
+ *
69
+ * 1. Read and verify the bootstrap token (before transaction)
70
+ * 2. Hash the password (CPU-intensive, before transaction)
71
+ * 3. Acquire the bootstrap lock atomically (inside transaction)
72
+ * 4. Create account + actor
73
+ * 5. Grant keeper and admin permits (no expiry, `granted_by = null`)
74
+ * 6. Delete the token file (after commit, reported via `token_file_deleted`)
75
+ *
76
+ * @param deps - database, token path, filesystem callbacks, and password hashing
77
+ * @param provided_token - the bootstrap token from the user
78
+ * @param input - username and password
79
+ * @returns the created account, actor, and permits — or a bootstrap failure
80
+ */
81
+ export declare const bootstrap_account: (deps: BootstrapAccountDeps, provided_token: string, input: BootstrapAccountInput) => Promise<BootstrapAccountResult>;
82
+ //# sourceMappingURL=bootstrap_account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap_account.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_account.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,eAAe,CAAC;AACpD,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,wBAAwB,EACxB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAC,MAAM,qBAAqB,CAAC;AAGhE,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,gDAAgD;AAChD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,6DAA6D;AAC7D,MAAM,WAAW,uBAAuB;IACvC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,CAAC;IACzC,wFAAwF;IACxF,kBAAkB,EAAE,OAAO,CAAC;CAC5B;AAED,gCAAgC;AAChC,MAAM,MAAM,uBAAuB,GAChC;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,0BAA0B,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAClE;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,wBAAwB,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,GAChE;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,mBAAmB,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,CAAC;AAE/D,qFAAqF;AACrF,MAAM,MAAM,sBAAsB,GAAG,uBAAuB,GAAG,uBAAuB,CAAC;AAEvF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,EAAE,EAAE,EAAE,CAAC;IACP,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,qBAAqB;IACrB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,6EAA6E;IAC7E,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;IAClD,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,oBAAoB,EAC1B,gBAAgB,MAAM,EACtB,OAAO,qBAAqB,KAC1B,OAAO,CAAC,sBAAsB,CA4EhC,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Bootstrap flow for creating the first account.
3
+ *
4
+ * Uses an atomic `bootstrap_lock` table to prevent TOCTOU race conditions.
5
+ * Token verification and account creation happen in a single transaction.
6
+ *
7
+ * @module
8
+ */
9
+ import { timingSafeEqual } from 'node:crypto';
10
+ import { ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, } from '../http/error_schemas.js';
11
+ import { ROLE_ADMIN, ROLE_KEEPER } from './role_schema.js';
12
+ import { query_create_account_with_actor, query_account_has_any } from './account_queries.js';
13
+ import { query_grant_permit } from './permit_queries.js';
14
+ /**
15
+ * Bootstrap the first account with keeper and admin privileges.
16
+ *
17
+ * Uses an atomic `bootstrap_lock` UPDATE to prevent concurrent bootstrap
18
+ * attempts (TOCTOU). The full flow runs in a single transaction:
19
+ *
20
+ * 1. Read and verify the bootstrap token (before transaction)
21
+ * 2. Hash the password (CPU-intensive, before transaction)
22
+ * 3. Acquire the bootstrap lock atomically (inside transaction)
23
+ * 4. Create account + actor
24
+ * 5. Grant keeper and admin permits (no expiry, `granted_by = null`)
25
+ * 6. Delete the token file (after commit, reported via `token_file_deleted`)
26
+ *
27
+ * @param deps - database, token path, filesystem callbacks, and password hashing
28
+ * @param provided_token - the bootstrap token from the user
29
+ * @param input - username and password
30
+ * @returns the created account, actor, and permits — or a bootstrap failure
31
+ */
32
+ export const bootstrap_account = async (deps, provided_token, input) => {
33
+ const { db, token_path, read_file, delete_file, password, log } = deps;
34
+ // 1. Read and verify token (non-destructive, before transaction)
35
+ let expected_token;
36
+ try {
37
+ expected_token = (await read_file(token_path)).trim();
38
+ }
39
+ catch {
40
+ return { ok: false, error: ERROR_TOKEN_FILE_MISSING, status: 404 };
41
+ }
42
+ // Defense-in-depth: no .trim() on provided_token — tokens must match exactly.
43
+ // The expected_token is already trimmed above.
44
+ const provided_buf = Buffer.from(provided_token);
45
+ const expected_buf = Buffer.from(expected_token);
46
+ if (provided_buf.length !== expected_buf.length || !timingSafeEqual(provided_buf, expected_buf)) {
47
+ return { ok: false, error: ERROR_INVALID_TOKEN, status: 401 };
48
+ }
49
+ // 2. Hash password (CPU-intensive, before transaction)
50
+ const password_hash = await password.hash_password(input.password);
51
+ // 3. Atomic transaction: lock + create
52
+ const tx_result = await db.transaction(async (tx) => {
53
+ const lock_rows = await tx.query('UPDATE bootstrap_lock SET bootstrapped = true WHERE id = 1 AND bootstrapped = false RETURNING id');
54
+ if (lock_rows.length === 0) {
55
+ return { ok: false, error: ERROR_ALREADY_BOOTSTRAPPED, status: 403 };
56
+ }
57
+ // Belt-and-suspenders: verify no accounts exist even if lock was available
58
+ if (await query_account_has_any({ db: tx })) {
59
+ return { ok: false, error: ERROR_ALREADY_BOOTSTRAPPED, status: 403 };
60
+ }
61
+ const tx_deps = { db: tx };
62
+ const { account, actor } = await query_create_account_with_actor(tx_deps, {
63
+ username: input.username,
64
+ password_hash,
65
+ });
66
+ const keeper_permit = await query_grant_permit(tx_deps, {
67
+ actor_id: actor.id,
68
+ role: ROLE_KEEPER,
69
+ granted_by: null,
70
+ expires_at: null,
71
+ });
72
+ const admin_permit = await query_grant_permit(tx_deps, {
73
+ actor_id: actor.id,
74
+ role: ROLE_ADMIN,
75
+ granted_by: null,
76
+ expires_at: null,
77
+ });
78
+ return {
79
+ ok: true,
80
+ account,
81
+ actor,
82
+ permits: { keeper: keeper_permit, admin: admin_permit },
83
+ };
84
+ });
85
+ if (!tx_result.ok)
86
+ return tx_result;
87
+ // 4. Delete token file (after commit)
88
+ let token_file_deleted = true;
89
+ try {
90
+ await delete_file(token_path);
91
+ }
92
+ catch {
93
+ token_file_deleted = false;
94
+ log.error(`CRITICAL: Failed to delete bootstrap token file at ${token_path}. Delete it manually.`);
95
+ }
96
+ return { ...tx_result, token_file_deleted };
97
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Bootstrap route spec for first-time account creation.
3
+ *
4
+ * One-shot endpoint: exchanges a bootstrap token + credentials for
5
+ * an account with keeper privileges and a session cookie.
6
+ *
7
+ * @module
8
+ */
9
+ import type { Context } from 'hono';
10
+ import type { Logger } from '@fuzdev/fuz_util/log.js';
11
+ import type { SessionOptions } from './session_cookie.js';
12
+ import { type BootstrapAccountSuccess } from './bootstrap_account.js';
13
+ import type { Db } from '../db/db.js';
14
+ import { type RouteSpec } from '../http/route_spec.js';
15
+ import { type RateLimiter } from '../rate_limiter.js';
16
+ import type { RouteFactoryDeps } from './deps.js';
17
+ import type { StatResult } from '../runtime/deps.js';
18
+ /**
19
+ * Bootstrap status — runtime state computed once at startup.
20
+ */
21
+ export interface BootstrapStatus {
22
+ available: boolean;
23
+ token_path: string | null;
24
+ }
25
+ /**
26
+ * Per-factory configuration for bootstrap route specs.
27
+ *
28
+ * `bootstrap_status` is runtime state (a mutable ref), not a dep or options value —
29
+ * it is passed through so the route handler can flip it on success.
30
+ */
31
+ export interface BootstrapRouteOptions {
32
+ session_options: SessionOptions<string>;
33
+ /** Shared mutable reference — flipped to false after successful bootstrap. */
34
+ bootstrap_status: BootstrapStatus;
35
+ /**
36
+ * Called after successful bootstrap (account + session created).
37
+ * Use for app-specific post-bootstrap work like generating API tokens.
38
+ */
39
+ on_bootstrap?: (result: BootstrapAccountSuccess, c: Context) => Promise<void>;
40
+ /** Rate limiter for bootstrap attempts (per-IP). Pass `null` to disable. */
41
+ ip_rate_limiter: RateLimiter | null;
42
+ }
43
+ /**
44
+ * Dependencies for checking bootstrap status at startup.
45
+ */
46
+ export interface CheckBootstrapStatusDeps {
47
+ stat: (path: string) => Promise<StatResult | null>;
48
+ db: Db;
49
+ log: Logger;
50
+ }
51
+ /**
52
+ * Check bootstrap availability at startup.
53
+ *
54
+ * Bootstrap is available when:
55
+ * 1. A token path is configured
56
+ * 2. The token file exists on disk
57
+ * 3. The `bootstrap_lock` table shows `bootstrapped = false`
58
+ *
59
+ * @param deps - filesystem and database access for the check
60
+ * @param options - static configuration including `token_path`
61
+ * @returns an object with `available` (boolean) and `token_path` (string | null)
62
+ */
63
+ export declare const check_bootstrap_status: (deps: CheckBootstrapStatusDeps, options: {
64
+ token_path: string | null;
65
+ }) => Promise<BootstrapStatus>;
66
+ /**
67
+ * Create bootstrap route specs for first-time account creation.
68
+ *
69
+ * @param deps - stateless capabilities including filesystem access
70
+ * @param options - per-factory configuration (session, token path, bootstrap status)
71
+ * @returns route specs (not yet applied to Hono)
72
+ */
73
+ export declare const create_bootstrap_route_specs: (deps: RouteFactoryDeps, options: BootstrapRouteOptions) => Array<RouteSpec>;
74
+ //# sourceMappingURL=bootstrap_routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/bootstrap_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,MAAM,CAAC;AAClC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAoB,KAAK,uBAAuB,EAAC,MAAM,wBAAwB,CAAC;AAGvF,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AACpC,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAClF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;AAenD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACrC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,8EAA8E;IAC9E,gBAAgB,EAAE,eAAe,CAAC;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,4EAA4E;IAC5E,eAAe,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,EAAE,EAAE,EAAE,CAAC;IACP,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,wBAAwB,EAC9B,SAAS;IAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,KAClC,OAAO,CAAC,eAAe,CAwBzB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,gBAAgB,EACtB,SAAS,qBAAqB,KAC5B,KAAK,CAAC,SAAS,CAsHjB,CAAC"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Bootstrap route spec for first-time account creation.
3
+ *
4
+ * One-shot endpoint: exchanges a bootstrap token + credentials for
5
+ * an account with keeper privileges and a session cookie.
6
+ *
7
+ * @module
8
+ */
9
+ import { z } from 'zod';
10
+ import { create_session_and_set_cookie } from './session_lifecycle.js';
11
+ import { bootstrap_account } from './bootstrap_account.js';
12
+ import { Username } from './account_schema.js';
13
+ import { Password } from './password.js';
14
+ import { get_route_input } from '../http/route_spec.js';
15
+ import { get_client_ip } from '../http/proxy.js';
16
+ import { rate_limit_exceeded_response } from '../rate_limiter.js';
17
+ import { ERROR_BOOTSTRAP_NOT_CONFIGURED, ERROR_INVALID_TOKEN, ERROR_ALREADY_BOOTSTRAPPED, ERROR_TOKEN_FILE_MISSING, } from '../http/error_schemas.js';
18
+ import { audit_log_fire_and_forget } from './audit_log_queries.js';
19
+ const bootstrap_input = z.strictObject({
20
+ token: z.string().min(1).meta({ sensitivity: 'secret' }),
21
+ username: Username,
22
+ password: Password,
23
+ });
24
+ /**
25
+ * Check bootstrap availability at startup.
26
+ *
27
+ * Bootstrap is available when:
28
+ * 1. A token path is configured
29
+ * 2. The token file exists on disk
30
+ * 3. The `bootstrap_lock` table shows `bootstrapped = false`
31
+ *
32
+ * @param deps - filesystem and database access for the check
33
+ * @param options - static configuration including `token_path`
34
+ * @returns an object with `available` (boolean) and `token_path` (string | null)
35
+ */
36
+ export const check_bootstrap_status = async (deps, options) => {
37
+ const { stat, db, log } = deps;
38
+ const { token_path } = options;
39
+ if (!token_path) {
40
+ return { available: false, token_path: null };
41
+ }
42
+ const token_stat = await stat(token_path);
43
+ if (token_stat === null) {
44
+ log.info('Bootstrap unavailable: token file not found');
45
+ return { available: false, token_path };
46
+ }
47
+ const lock_row = await db.query_one('SELECT bootstrapped FROM bootstrap_lock WHERE id = 1');
48
+ if (lock_row?.bootstrapped) {
49
+ log.info('Bootstrap unavailable: already bootstrapped');
50
+ return { available: false, token_path };
51
+ }
52
+ log.info(`Bootstrap token available: ${token_path}`);
53
+ return { available: true, token_path };
54
+ };
55
+ /**
56
+ * Create bootstrap route specs for first-time account creation.
57
+ *
58
+ * @param deps - stateless capabilities including filesystem access
59
+ * @param options - per-factory configuration (session, token path, bootstrap status)
60
+ * @returns route specs (not yet applied to Hono)
61
+ */
62
+ export const create_bootstrap_route_specs = (deps, options) => {
63
+ const { keyring, on_audit_event } = deps;
64
+ const { session_options, bootstrap_status, on_bootstrap, ip_rate_limiter } = options;
65
+ const { token_path } = bootstrap_status;
66
+ return [
67
+ {
68
+ method: 'POST',
69
+ path: '/bootstrap',
70
+ auth: { type: 'none' },
71
+ description: 'Create initial keeper account (one-shot)',
72
+ transaction: false, // bootstrap_account manages its own transaction
73
+ input: bootstrap_input,
74
+ output: z.strictObject({ ok: z.literal(true), username: z.string() }),
75
+ rate_limit: 'ip',
76
+ errors: {
77
+ 401: z.looseObject({ error: z.literal(ERROR_INVALID_TOKEN) }),
78
+ 403: z.looseObject({ error: z.literal(ERROR_ALREADY_BOOTSTRAPPED) }),
79
+ 404: z.looseObject({
80
+ error: z.enum([ERROR_TOKEN_FILE_MISSING, ERROR_BOOTSTRAP_NOT_CONFIGURED]),
81
+ }),
82
+ },
83
+ handler: async (c, route) => {
84
+ // Short-circuit if bootstrap already completed
85
+ if (!bootstrap_status.available) {
86
+ return c.json({ error: ERROR_ALREADY_BOOTSTRAPPED }, 403);
87
+ }
88
+ // Per-IP rate limit check (before any token/DB work)
89
+ const ip = ip_rate_limiter ? get_client_ip(c) : null;
90
+ if (ip_rate_limiter && ip) {
91
+ const check = ip_rate_limiter.check(ip);
92
+ if (!check.allowed) {
93
+ return rate_limit_exceeded_response(c, check.retry_after);
94
+ }
95
+ }
96
+ const input = get_route_input(c);
97
+ if (token_path === null) {
98
+ return c.json({ error: ERROR_BOOTSTRAP_NOT_CONFIGURED }, 404);
99
+ }
100
+ const result = await bootstrap_account({
101
+ db: route.background_db,
102
+ token_path,
103
+ read_file: deps.read_file,
104
+ delete_file: deps.delete_file,
105
+ password: deps.password,
106
+ log: deps.log,
107
+ }, input.token, input);
108
+ if (!result.ok) {
109
+ if (ip_rate_limiter && ip)
110
+ ip_rate_limiter.record(ip);
111
+ void audit_log_fire_and_forget(route, {
112
+ event_type: 'bootstrap',
113
+ outcome: 'failure',
114
+ ip: get_client_ip(c),
115
+ metadata: { error: result.error },
116
+ }, deps.log, on_audit_event);
117
+ return c.json({ error: result.error }, result.status);
118
+ }
119
+ // Successful bootstrap — update state immediately
120
+ if (ip_rate_limiter && ip)
121
+ ip_rate_limiter.reset(ip);
122
+ bootstrap_status.available = false;
123
+ await create_session_and_set_cookie({
124
+ keyring,
125
+ deps: { db: route.background_db },
126
+ c,
127
+ account_id: result.account.id,
128
+ session_options,
129
+ });
130
+ if (on_bootstrap) {
131
+ try {
132
+ await on_bootstrap(result, c);
133
+ }
134
+ catch (err) {
135
+ deps.log.error(`on_bootstrap callback failed: ${err instanceof Error ? err.message : String(err)}`);
136
+ }
137
+ }
138
+ void audit_log_fire_and_forget(route, {
139
+ event_type: 'bootstrap',
140
+ actor_id: result.actor.id,
141
+ account_id: result.account.id,
142
+ ip: get_client_ip(c),
143
+ }, deps.log, on_audit_event);
144
+ // CRITICAL: If token file deletion failed, throw to force operator attention.
145
+ // All success work (session, on_bootstrap, audit) has completed above.
146
+ // The error response alerts the operator to delete the token file manually.
147
+ if (!result.token_file_deleted) {
148
+ throw new Error(`Bootstrap succeeded but token file was not deleted at ${token_path}. Delete it manually and log in.`);
149
+ }
150
+ return c.json({ ok: true, username: result.account.username });
151
+ },
152
+ },
153
+ ];
154
+ };