@braidhq/server 0.0.1 → 0.2.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 (376) hide show
  1. package/README.md +164 -0
  2. package/dist/app.d.ts +12 -3
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +154 -9
  5. package/dist/app.js.map +1 -1
  6. package/dist/authMode.d.ts +39 -0
  7. package/dist/authMode.d.ts.map +1 -0
  8. package/dist/authMode.js +50 -0
  9. package/dist/authMode.js.map +1 -0
  10. package/dist/composeApp.d.ts +79 -0
  11. package/dist/composeApp.d.ts.map +1 -0
  12. package/dist/composeApp.js +136 -0
  13. package/dist/composeApp.js.map +1 -0
  14. package/dist/composeFsApp.d.ts +27 -0
  15. package/dist/composeFsApp.d.ts.map +1 -0
  16. package/dist/composeFsApp.js +309 -0
  17. package/dist/composeFsApp.js.map +1 -0
  18. package/dist/index.d.ts +3 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/infrastructure/_shared/env.d.ts +10 -0
  23. package/dist/infrastructure/_shared/env.d.ts.map +1 -0
  24. package/dist/infrastructure/_shared/env.js +19 -0
  25. package/dist/infrastructure/_shared/env.js.map +1 -0
  26. package/dist/infrastructure/_shared/frontmatter.d.ts +13 -0
  27. package/dist/infrastructure/_shared/frontmatter.d.ts.map +1 -0
  28. package/dist/infrastructure/{fs → _shared}/frontmatter.js +5 -5
  29. package/dist/infrastructure/_shared/frontmatter.js.map +1 -0
  30. package/dist/infrastructure/_shared/jsonFileStore.d.ts.map +1 -0
  31. package/dist/infrastructure/_shared/jsonFileStore.js.map +1 -0
  32. package/dist/infrastructure/_shared/paths.d.ts +43 -0
  33. package/dist/infrastructure/_shared/paths.d.ts.map +1 -0
  34. package/dist/infrastructure/_shared/paths.js +96 -0
  35. package/dist/infrastructure/_shared/paths.js.map +1 -0
  36. package/dist/infrastructure/auth/AccessPolicy.d.ts +87 -0
  37. package/dist/infrastructure/auth/AccessPolicy.d.ts.map +1 -0
  38. package/dist/infrastructure/auth/AccessPolicy.js +177 -0
  39. package/dist/infrastructure/auth/AccessPolicy.js.map +1 -0
  40. package/dist/infrastructure/auth/SessionStore.d.ts +50 -0
  41. package/dist/infrastructure/auth/SessionStore.d.ts.map +1 -0
  42. package/dist/infrastructure/auth/SessionStore.js +104 -0
  43. package/dist/infrastructure/auth/SessionStore.js.map +1 -0
  44. package/dist/infrastructure/batch/FsBatchPlanRepository.d.ts +79 -0
  45. package/dist/infrastructure/batch/FsBatchPlanRepository.d.ts.map +1 -0
  46. package/dist/infrastructure/batch/FsBatchPlanRepository.js +42 -0
  47. package/dist/infrastructure/batch/FsBatchPlanRepository.js.map +1 -0
  48. package/dist/infrastructure/history/GitWorkspaceHistory.d.ts +18 -0
  49. package/dist/infrastructure/history/GitWorkspaceHistory.d.ts.map +1 -0
  50. package/dist/infrastructure/history/GitWorkspaceHistory.js +310 -0
  51. package/dist/infrastructure/history/GitWorkspaceHistory.js.map +1 -0
  52. package/dist/infrastructure/history/commitMessage.d.ts +4 -0
  53. package/dist/infrastructure/history/commitMessage.d.ts.map +1 -0
  54. package/dist/infrastructure/history/commitMessage.js +90 -0
  55. package/dist/infrastructure/history/commitMessage.js.map +1 -0
  56. package/dist/infrastructure/hitl/FsClarificationRepository.d.ts +13 -0
  57. package/dist/infrastructure/hitl/FsClarificationRepository.d.ts.map +1 -0
  58. package/dist/infrastructure/hitl/FsClarificationRepository.js +39 -0
  59. package/dist/infrastructure/hitl/FsClarificationRepository.js.map +1 -0
  60. package/dist/infrastructure/hitl/FsProposalRepository.d.ts.map +1 -0
  61. package/dist/infrastructure/{fs → hitl}/FsProposalRepository.js +10 -3
  62. package/dist/infrastructure/hitl/FsProposalRepository.js.map +1 -0
  63. package/dist/infrastructure/hitl/StatusedJsonStore.d.ts +30 -0
  64. package/dist/infrastructure/hitl/StatusedJsonStore.d.ts.map +1 -0
  65. package/dist/infrastructure/{fs/FsStatusedJsonRepository.js → hitl/StatusedJsonStore.js} +3 -3
  66. package/dist/infrastructure/hitl/StatusedJsonStore.js.map +1 -0
  67. package/dist/infrastructure/model/FsModelSerializer.d.ts +96 -0
  68. package/dist/infrastructure/model/FsModelSerializer.d.ts.map +1 -0
  69. package/dist/infrastructure/model/FsModelSerializer.js +60 -0
  70. package/dist/infrastructure/model/FsModelSerializer.js.map +1 -0
  71. package/dist/infrastructure/oauth/GoogleOAuth.d.ts +43 -14
  72. package/dist/infrastructure/oauth/GoogleOAuth.d.ts.map +1 -1
  73. package/dist/infrastructure/oauth/GoogleOAuth.js +78 -11
  74. package/dist/infrastructure/oauth/GoogleOAuth.js.map +1 -1
  75. package/dist/infrastructure/reactor/FsReactorCycleRepository.d.ts +29 -0
  76. package/dist/infrastructure/reactor/FsReactorCycleRepository.d.ts.map +1 -0
  77. package/dist/infrastructure/reactor/FsReactorCycleRepository.js +94 -0
  78. package/dist/infrastructure/reactor/FsReactorCycleRepository.js.map +1 -0
  79. package/dist/infrastructure/secrets/SecretStore.d.ts +11 -14
  80. package/dist/infrastructure/secrets/SecretStore.d.ts.map +1 -1
  81. package/dist/infrastructure/secrets/SecretStore.js +4 -4
  82. package/dist/infrastructure/secrets/SecretStore.js.map +1 -1
  83. package/dist/infrastructure/{fs → skill}/FsRunRepository.d.ts +9 -4
  84. package/dist/infrastructure/skill/FsRunRepository.d.ts.map +1 -0
  85. package/dist/infrastructure/skill/FsRunRepository.js +162 -0
  86. package/dist/infrastructure/skill/FsRunRepository.js.map +1 -0
  87. package/dist/infrastructure/{fs → skill}/FsSkillRegistry.d.ts +4 -13
  88. package/dist/infrastructure/skill/FsSkillRegistry.d.ts.map +1 -0
  89. package/dist/infrastructure/{fs → skill}/FsSkillRegistry.js +38 -18
  90. package/dist/infrastructure/skill/FsSkillRegistry.js.map +1 -0
  91. package/dist/infrastructure/{agent → skill}/SubprocessSkillRunner.d.ts +13 -15
  92. package/dist/infrastructure/skill/SubprocessSkillRunner.d.ts.map +1 -0
  93. package/dist/infrastructure/{agent → skill}/SubprocessSkillRunner.js +124 -40
  94. package/dist/infrastructure/skill/SubprocessSkillRunner.js.map +1 -0
  95. package/dist/infrastructure/skill/asyncQueue.d.ts +20 -0
  96. package/dist/infrastructure/skill/asyncQueue.d.ts.map +1 -0
  97. package/dist/infrastructure/{agent → skill}/asyncQueue.js +2 -3
  98. package/dist/infrastructure/{agent → skill}/asyncQueue.js.map +1 -1
  99. package/dist/infrastructure/skill/lineBuffer.d.ts +15 -0
  100. package/dist/infrastructure/skill/lineBuffer.d.ts.map +1 -0
  101. package/dist/infrastructure/{agent/streamJsonParser.js → skill/lineBuffer.js} +5 -17
  102. package/dist/infrastructure/skill/lineBuffer.js.map +1 -0
  103. package/dist/infrastructure/skill/orphanReaper.d.ts +20 -0
  104. package/dist/infrastructure/skill/orphanReaper.d.ts.map +1 -0
  105. package/dist/infrastructure/{orphanReaper.js → skill/orphanReaper.js} +7 -5
  106. package/dist/infrastructure/skill/orphanReaper.js.map +1 -0
  107. package/dist/infrastructure/skill/subprocessEventStream.d.ts +16 -0
  108. package/dist/infrastructure/skill/subprocessEventStream.d.ts.map +1 -0
  109. package/dist/infrastructure/skill/subprocessEventStream.js +29 -0
  110. package/dist/infrastructure/skill/subprocessEventStream.js.map +1 -0
  111. package/dist/infrastructure/source/FsSourceUnitDigest.d.ts +19 -0
  112. package/dist/infrastructure/source/FsSourceUnitDigest.d.ts.map +1 -0
  113. package/dist/infrastructure/source/FsSourceUnitDigest.js +58 -0
  114. package/dist/infrastructure/source/FsSourceUnitDigest.js.map +1 -0
  115. package/dist/infrastructure/source/FsSourceUnitObservationRepository.d.ts +31 -0
  116. package/dist/infrastructure/source/FsSourceUnitObservationRepository.d.ts.map +1 -0
  117. package/dist/infrastructure/source/FsSourceUnitObservationRepository.js +88 -0
  118. package/dist/infrastructure/source/FsSourceUnitObservationRepository.js.map +1 -0
  119. package/dist/infrastructure/source/intentScan.d.ts +13 -0
  120. package/dist/infrastructure/source/intentScan.d.ts.map +1 -0
  121. package/dist/infrastructure/source/intentScan.js +137 -0
  122. package/dist/infrastructure/source/intentScan.js.map +1 -0
  123. package/dist/infrastructure/users/UserDirectoryFromRegistry.d.ts +14 -0
  124. package/dist/infrastructure/users/UserDirectoryFromRegistry.d.ts.map +1 -0
  125. package/dist/infrastructure/users/UserDirectoryFromRegistry.js +21 -0
  126. package/dist/infrastructure/users/UserDirectoryFromRegistry.js.map +1 -0
  127. package/dist/infrastructure/users/UserRegistryFile.d.ts +22 -0
  128. package/dist/infrastructure/users/UserRegistryFile.d.ts.map +1 -0
  129. package/dist/infrastructure/users/UserRegistryFile.js +89 -0
  130. package/dist/infrastructure/users/UserRegistryFile.js.map +1 -0
  131. package/dist/infrastructure/users/ensureWorkspaceOwners.d.ts +12 -0
  132. package/dist/infrastructure/users/ensureWorkspaceOwners.d.ts.map +1 -0
  133. package/dist/infrastructure/users/ensureWorkspaceOwners.js +25 -0
  134. package/dist/infrastructure/users/ensureWorkspaceOwners.js.map +1 -0
  135. package/dist/infrastructure/workspace/FsWorkspaceRepository.d.ts.map +1 -0
  136. package/dist/infrastructure/{fs → workspace}/FsWorkspaceRepository.js +15 -5
  137. package/dist/infrastructure/workspace/FsWorkspaceRepository.js.map +1 -0
  138. package/dist/infrastructure/workspace/WorkspaceDiscovery.d.ts +18 -0
  139. package/dist/infrastructure/workspace/WorkspaceDiscovery.d.ts.map +1 -0
  140. package/dist/infrastructure/workspace/WorkspaceDiscovery.js +50 -0
  141. package/dist/infrastructure/workspace/WorkspaceDiscovery.js.map +1 -0
  142. package/dist/infrastructure/workspace/WorkspaceRegistryFile.d.ts +42 -0
  143. package/dist/infrastructure/workspace/WorkspaceRegistryFile.d.ts.map +1 -0
  144. package/dist/infrastructure/workspace/WorkspaceRegistryFile.js +160 -0
  145. package/dist/infrastructure/workspace/WorkspaceRegistryFile.js.map +1 -0
  146. package/dist/infrastructure/workspace/productManifestWriter.d.ts +24 -0
  147. package/dist/infrastructure/workspace/productManifestWriter.d.ts.map +1 -0
  148. package/dist/infrastructure/{fs → workspace}/productManifestWriter.js +14 -28
  149. package/dist/infrastructure/workspace/productManifestWriter.js.map +1 -0
  150. package/dist/middleware/auth.d.ts +39 -0
  151. package/dist/middleware/auth.d.ts.map +1 -0
  152. package/dist/middleware/auth.js +87 -0
  153. package/dist/middleware/auth.js.map +1 -0
  154. package/dist/middleware/cors.d.ts.map +1 -1
  155. package/dist/middleware/cors.js +5 -2
  156. package/dist/middleware/cors.js.map +1 -1
  157. package/dist/middleware/error.d.ts.map +1 -1
  158. package/dist/middleware/error.js +15 -13
  159. package/dist/middleware/error.js.map +1 -1
  160. package/dist/middleware/workspaceAccess.d.ts +51 -0
  161. package/dist/middleware/workspaceAccess.d.ts.map +1 -0
  162. package/dist/middleware/workspaceAccess.js +91 -0
  163. package/dist/middleware/workspaceAccess.js.map +1 -0
  164. package/dist/policy/CapabilityCheck.d.ts +14 -0
  165. package/dist/policy/CapabilityCheck.d.ts.map +1 -0
  166. package/dist/policy/CapabilityCheck.js +2 -0
  167. package/dist/policy/CapabilityCheck.js.map +1 -0
  168. package/dist/policy/PermissionRegistry.d.ts +25 -0
  169. package/dist/policy/PermissionRegistry.d.ts.map +1 -0
  170. package/dist/policy/PermissionRegistry.js +29 -0
  171. package/dist/policy/PermissionRegistry.js.map +1 -0
  172. package/dist/policy/ViewerContext.d.ts +29 -0
  173. package/dist/policy/ViewerContext.d.ts.map +1 -0
  174. package/dist/policy/ViewerContext.js +2 -0
  175. package/dist/policy/ViewerContext.js.map +1 -0
  176. package/dist/policy/checks.d.ts +15 -0
  177. package/dist/policy/checks.d.ts.map +1 -0
  178. package/dist/policy/checks.js +55 -0
  179. package/dist/policy/checks.js.map +1 -0
  180. package/dist/policy/defaultRegistry.d.ts +9 -0
  181. package/dist/policy/defaultRegistry.d.ts.map +1 -0
  182. package/dist/policy/defaultRegistry.js +15 -0
  183. package/dist/policy/defaultRegistry.js.map +1 -0
  184. package/dist/policy/index.d.ts +8 -0
  185. package/dist/policy/index.d.ts.map +1 -0
  186. package/dist/policy/index.js +5 -0
  187. package/dist/policy/index.js.map +1 -0
  188. package/dist/policy/resolveViewer.d.ts +16 -0
  189. package/dist/policy/resolveViewer.d.ts.map +1 -0
  190. package/dist/policy/resolveViewer.js +37 -0
  191. package/dist/policy/resolveViewer.js.map +1 -0
  192. package/dist/routes/_shared.d.ts +138 -0
  193. package/dist/routes/_shared.d.ts.map +1 -0
  194. package/dist/routes/_shared.js +33 -0
  195. package/dist/routes/_shared.js.map +1 -0
  196. package/dist/routes/admin.d.ts +13 -0
  197. package/dist/routes/admin.d.ts.map +1 -0
  198. package/dist/routes/admin.js +228 -0
  199. package/dist/routes/admin.js.map +1 -0
  200. package/dist/routes/auth.d.ts +35 -0
  201. package/dist/routes/auth.d.ts.map +1 -0
  202. package/dist/routes/auth.js +168 -0
  203. package/dist/routes/auth.js.map +1 -0
  204. package/dist/routes/batch.d.ts +7 -0
  205. package/dist/routes/batch.d.ts.map +1 -0
  206. package/dist/routes/batch.js +47 -0
  207. package/dist/routes/batch.js.map +1 -0
  208. package/dist/routes/clarifications.d.ts +8 -0
  209. package/dist/routes/clarifications.d.ts.map +1 -0
  210. package/dist/routes/clarifications.js +243 -0
  211. package/dist/routes/clarifications.js.map +1 -0
  212. package/dist/routes/edges.d.ts +2 -2
  213. package/dist/routes/edges.d.ts.map +1 -1
  214. package/dist/routes/edges.js +34 -16
  215. package/dist/routes/edges.js.map +1 -1
  216. package/dist/routes/health.d.ts +2 -2
  217. package/dist/routes/health.d.ts.map +1 -1
  218. package/dist/routes/health.js +21 -3
  219. package/dist/routes/health.js.map +1 -1
  220. package/dist/routes/history.d.ts +7 -0
  221. package/dist/routes/history.d.ts.map +1 -0
  222. package/dist/routes/history.js +90 -0
  223. package/dist/routes/history.js.map +1 -0
  224. package/dist/routes/model.d.ts +2 -2
  225. package/dist/routes/model.d.ts.map +1 -1
  226. package/dist/routes/model.js +20 -4
  227. package/dist/routes/model.js.map +1 -1
  228. package/dist/routes/nodes.d.ts +2 -2
  229. package/dist/routes/nodes.d.ts.map +1 -1
  230. package/dist/routes/nodes.js +77 -25
  231. package/dist/routes/nodes.js.map +1 -1
  232. package/dist/routes/oauth.d.ts +6 -7
  233. package/dist/routes/oauth.d.ts.map +1 -1
  234. package/dist/routes/oauth.js +6 -7
  235. package/dist/routes/oauth.js.map +1 -1
  236. package/dist/routes/ontology.d.ts +2 -2
  237. package/dist/routes/ontology.d.ts.map +1 -1
  238. package/dist/routes/ontology.js +22 -5
  239. package/dist/routes/ontology.js.map +1 -1
  240. package/dist/routes/proposals.d.ts +5 -4
  241. package/dist/routes/proposals.d.ts.map +1 -1
  242. package/dist/routes/proposals.js +176 -45
  243. package/dist/routes/proposals.js.map +1 -1
  244. package/dist/routes/reactorCycles.d.ts +7 -0
  245. package/dist/routes/reactorCycles.d.ts.map +1 -0
  246. package/dist/routes/reactorCycles.js +60 -0
  247. package/dist/routes/reactorCycles.js.map +1 -0
  248. package/dist/routes/runs.d.ts.map +1 -1
  249. package/dist/routes/runs.js +66 -12
  250. package/dist/routes/runs.js.map +1 -1
  251. package/dist/routes/skillInputOptions.d.ts +10 -0
  252. package/dist/routes/skillInputOptions.d.ts.map +1 -0
  253. package/dist/routes/skillInputOptions.js +159 -0
  254. package/dist/routes/skillInputOptions.js.map +1 -0
  255. package/dist/routes/skills.d.ts +21 -3
  256. package/dist/routes/skills.d.ts.map +1 -1
  257. package/dist/routes/skills.js +223 -21
  258. package/dist/routes/skills.js.map +1 -1
  259. package/dist/routes/sourceLoaders.d.ts +7 -0
  260. package/dist/routes/sourceLoaders.d.ts.map +1 -0
  261. package/dist/routes/sourceLoaders.js +30 -0
  262. package/dist/routes/sourceLoaders.js.map +1 -0
  263. package/dist/routes/sourceUnitObservations.d.ts +20 -0
  264. package/dist/routes/sourceUnitObservations.d.ts.map +1 -0
  265. package/dist/routes/sourceUnitObservations.js +80 -0
  266. package/dist/routes/sourceUnitObservations.js.map +1 -0
  267. package/dist/routes/sourceWebhooks.d.ts +26 -0
  268. package/dist/routes/sourceWebhooks.d.ts.map +1 -0
  269. package/dist/routes/sourceWebhooks.js +301 -0
  270. package/dist/routes/sourceWebhooks.js.map +1 -0
  271. package/dist/routes/users.d.ts +7 -0
  272. package/dist/routes/users.d.ts.map +1 -0
  273. package/dist/routes/users.js +109 -0
  274. package/dist/routes/users.js.map +1 -0
  275. package/dist/routes/workspaceEvents.d.ts +5 -4
  276. package/dist/routes/workspaceEvents.d.ts.map +1 -1
  277. package/dist/routes/workspaceEvents.js +7 -6
  278. package/dist/routes/workspaceEvents.js.map +1 -1
  279. package/dist/routes/workspaceMembers.d.ts +11 -0
  280. package/dist/routes/workspaceMembers.d.ts.map +1 -0
  281. package/dist/routes/workspaceMembers.js +81 -0
  282. package/dist/routes/workspaceMembers.js.map +1 -0
  283. package/dist/routes/workspaces.d.ts +30 -1
  284. package/dist/routes/workspaces.d.ts.map +1 -1
  285. package/dist/routes/workspaces.js +207 -63
  286. package/dist/routes/workspaces.js.map +1 -1
  287. package/dist/server.js +9 -29
  288. package/dist/server.js.map +1 -1
  289. package/dist/startServer.d.ts +17 -0
  290. package/dist/startServer.d.ts.map +1 -0
  291. package/dist/startServer.js +81 -0
  292. package/dist/startServer.js.map +1 -0
  293. package/dist/startup.d.ts +28 -0
  294. package/dist/startup.d.ts.map +1 -0
  295. package/dist/startup.js +94 -0
  296. package/dist/startup.js.map +1 -0
  297. package/package.json +49 -32
  298. package/LICENSE +0 -21
  299. package/dist/composeFs.d.ts +0 -20
  300. package/dist/composeFs.d.ts.map +0 -1
  301. package/dist/composeFs.js +0 -134
  302. package/dist/composeFs.js.map +0 -1
  303. package/dist/composition.d.ts +0 -49
  304. package/dist/composition.d.ts.map +0 -1
  305. package/dist/composition.js +0 -43
  306. package/dist/composition.js.map +0 -1
  307. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.d.ts +0 -8
  308. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.d.ts.map +0 -1
  309. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.js +0 -62
  310. package/dist/infrastructure/agent/ClaudeCodeAgentBinding.js.map +0 -1
  311. package/dist/infrastructure/agent/SubprocessSkillRunner.d.ts.map +0 -1
  312. package/dist/infrastructure/agent/SubprocessSkillRunner.js.map +0 -1
  313. package/dist/infrastructure/agent/asyncQueue.d.ts +0 -22
  314. package/dist/infrastructure/agent/asyncQueue.d.ts.map +0 -1
  315. package/dist/infrastructure/agent/mcpConfig.d.ts +0 -18
  316. package/dist/infrastructure/agent/mcpConfig.d.ts.map +0 -1
  317. package/dist/infrastructure/agent/mcpConfig.js +0 -47
  318. package/dist/infrastructure/agent/mcpConfig.js.map +0 -1
  319. package/dist/infrastructure/agent/streamJsonParser.d.ts +0 -20
  320. package/dist/infrastructure/agent/streamJsonParser.d.ts.map +0 -1
  321. package/dist/infrastructure/agent/streamJsonParser.js.map +0 -1
  322. package/dist/infrastructure/agent/subprocessEventStream.d.ts +0 -26
  323. package/dist/infrastructure/agent/subprocessEventStream.d.ts.map +0 -1
  324. package/dist/infrastructure/agent/subprocessEventStream.js +0 -112
  325. package/dist/infrastructure/agent/subprocessEventStream.js.map +0 -1
  326. package/dist/infrastructure/fs/FsClarifyTicketRepository.d.ts +0 -13
  327. package/dist/infrastructure/fs/FsClarifyTicketRepository.d.ts.map +0 -1
  328. package/dist/infrastructure/fs/FsClarifyTicketRepository.js +0 -33
  329. package/dist/infrastructure/fs/FsClarifyTicketRepository.js.map +0 -1
  330. package/dist/infrastructure/fs/FsDecisionRepository.d.ts +0 -13
  331. package/dist/infrastructure/fs/FsDecisionRepository.d.ts.map +0 -1
  332. package/dist/infrastructure/fs/FsDecisionRepository.js +0 -56
  333. package/dist/infrastructure/fs/FsDecisionRepository.js.map +0 -1
  334. package/dist/infrastructure/fs/FsProposalRepository.d.ts.map +0 -1
  335. package/dist/infrastructure/fs/FsProposalRepository.js.map +0 -1
  336. package/dist/infrastructure/fs/FsRunRepository.d.ts.map +0 -1
  337. package/dist/infrastructure/fs/FsRunRepository.js +0 -88
  338. package/dist/infrastructure/fs/FsRunRepository.js.map +0 -1
  339. package/dist/infrastructure/fs/FsSkillRegistry.d.ts.map +0 -1
  340. package/dist/infrastructure/fs/FsSkillRegistry.js.map +0 -1
  341. package/dist/infrastructure/fs/FsStatusedJsonRepository.d.ts +0 -35
  342. package/dist/infrastructure/fs/FsStatusedJsonRepository.d.ts.map +0 -1
  343. package/dist/infrastructure/fs/FsStatusedJsonRepository.js.map +0 -1
  344. package/dist/infrastructure/fs/FsWorkspaceRepository.d.ts.map +0 -1
  345. package/dist/infrastructure/fs/FsWorkspaceRepository.js.map +0 -1
  346. package/dist/infrastructure/fs/WorkspaceRegistryFile.d.ts +0 -17
  347. package/dist/infrastructure/fs/WorkspaceRegistryFile.d.ts.map +0 -1
  348. package/dist/infrastructure/fs/WorkspaceRegistryFile.js +0 -58
  349. package/dist/infrastructure/fs/WorkspaceRegistryFile.js.map +0 -1
  350. package/dist/infrastructure/fs/frontmatter.d.ts +0 -13
  351. package/dist/infrastructure/fs/frontmatter.d.ts.map +0 -1
  352. package/dist/infrastructure/fs/frontmatter.js.map +0 -1
  353. package/dist/infrastructure/fs/jsonFileStore.d.ts.map +0 -1
  354. package/dist/infrastructure/fs/jsonFileStore.js.map +0 -1
  355. package/dist/infrastructure/fs/paths.d.ts +0 -22
  356. package/dist/infrastructure/fs/paths.d.ts.map +0 -1
  357. package/dist/infrastructure/fs/paths.js +0 -46
  358. package/dist/infrastructure/fs/paths.js.map +0 -1
  359. package/dist/infrastructure/fs/productManifestWriter.d.ts +0 -22
  360. package/dist/infrastructure/fs/productManifestWriter.d.ts.map +0 -1
  361. package/dist/infrastructure/fs/productManifestWriter.js.map +0 -1
  362. package/dist/infrastructure/orphanReaper.d.ts +0 -18
  363. package/dist/infrastructure/orphanReaper.d.ts.map +0 -1
  364. package/dist/infrastructure/orphanReaper.js.map +0 -1
  365. package/dist/routes/clarify.d.ts +0 -8
  366. package/dist/routes/clarify.d.ts.map +0 -1
  367. package/dist/routes/clarify.js +0 -84
  368. package/dist/routes/clarify.js.map +0 -1
  369. package/dist/routes/decisions.d.ts +0 -7
  370. package/dist/routes/decisions.d.ts.map +0 -1
  371. package/dist/routes/decisions.js +0 -30
  372. package/dist/routes/decisions.js.map +0 -1
  373. /package/dist/infrastructure/{fs → _shared}/jsonFileStore.d.ts +0 -0
  374. /package/dist/infrastructure/{fs → _shared}/jsonFileStore.js +0 -0
  375. /package/dist/infrastructure/{fs → hitl}/FsProposalRepository.d.ts +0 -0
  376. /package/dist/infrastructure/{fs → workspace}/FsWorkspaceRepository.d.ts +0 -0
@@ -0,0 +1,43 @@
1
+ import type { AbsolutePath, ClarificationStatus, ProposalStatus, WorkspaceId } from '@braidhq/schema';
2
+ export declare const PROPOSAL_STATUSES: readonly ProposalStatus[];
3
+ export declare const CLARIFY_STATUSES: readonly ClarificationStatus[];
4
+ /**
5
+ * Path-prefix containment check. Both inputs must be absolute.
6
+ * Handles the `/foo/bar-evil` vs `/foo/bar/` aliasing gotcha,
7
+ * normalising the parent with a trailing slash before `startsWith`.
8
+ */
9
+ export declare function isUnder(candidate: string, parent: string): boolean;
10
+ /** `true` when `path` resolves to anything (file, dir, symlink). */
11
+ export declare function pathExists(path: string): Promise<boolean>;
12
+ export declare function workspaceArtifactsDir(workspaceRoot: AbsolutePath): string;
13
+ export declare function graphJsonPath(workspaceRoot: AbsolutePath): string;
14
+ export declare function batchPlanPath(workspaceRoot: AbsolutePath): string;
15
+ export declare function sourceUnitObservationDir(workspaceRoot: AbsolutePath): string;
16
+ export declare function sourceUnitObservationFilePath(workspaceRoot: AbsolutePath, sourceId: string, relativePath: string): string;
17
+ export declare function sourceUnitObservationSourceDir(workspaceRoot: AbsolutePath, sourceId: string): string;
18
+ export declare function proposalsDir(workspaceRoot: AbsolutePath, status: ProposalStatus): string;
19
+ export declare function clarificationDir(workspaceRoot: AbsolutePath, status: ClarificationStatus): string;
20
+ export declare function viewsDir(workspaceRoot: AbsolutePath, kind: string): string;
21
+ export declare function runsDir(workspaceRoot: AbsolutePath): string;
22
+ export declare function reactorCyclesDir(workspaceRoot: AbsolutePath): string;
23
+ export declare function reactorCycleFilePath(workspaceRoot: AbsolutePath, cycleId: string): string;
24
+ export declare function runIndexPath(workspaceRoot: AbsolutePath): string;
25
+ export declare function runEventsPath(workspaceRoot: AbsolutePath, runId: string): string;
26
+ export declare function runSessionsMetadataPath(workspaceRoot: AbsolutePath): string;
27
+ /**
28
+ * claude stores conversation memory keyed by cwd,
29
+ * so resume must spawn from the same dir as the first turn.
30
+ * Keeping it in the workspace, not /tmp, stays derivable from runId,
31
+ * and survives a server restart.
32
+ */
33
+ export declare function sessionsDir(workspaceRoot: AbsolutePath): string;
34
+ export declare function sessionDirPath(workspaceRoot: AbsolutePath, runId: string): string;
35
+ export declare function workspaceSkillsDir(workspaceRoot: AbsolutePath): string;
36
+ export declare function workspaceSkillExtensionsDir(workspaceRoot: AbsolutePath): string;
37
+ export declare function workspaceProductManifestPath(workspaceRoot: AbsolutePath): string;
38
+ export interface WorkspaceLayout {
39
+ readonly root: AbsolutePath;
40
+ readonly workspaceId: WorkspaceId;
41
+ readonly manifestPath: string;
42
+ }
43
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/_shared/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAIrG,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAAuC,CAAA;AAC9F,eAAO,MAAM,gBAAgB,EAAE,SAAS,mBAAmB,EAAkD,CAAA;AAE7G;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGlE;AAED,oEAAoE;AACpE,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ/D;AAED,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEzE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE5E;AAED,wBAAgB,6BAA6B,CAC3C,aAAa,EAAE,YAAY,EAC3B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAQR;AAED,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,YAAY,EAC3B,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAExF;AAED,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAEjG;AAED,wBAAgB,QAAQ,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,wBAAgB,OAAO,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE3D;AAED,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzF;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEhE;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE3E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE/D;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEtE;AAED,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAE/E;AAED,wBAAgB,4BAA4B,CAAC,aAAa,EAAE,YAAY,GAAG,MAAM,CAEhF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B"}
@@ -0,0 +1,96 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ export const PROPOSAL_STATUSES = ['pending', 'applied', 'rejected'];
4
+ export const CLARIFY_STATUSES = ['pending', 'answered', 'applied', 'skipped'];
5
+ /**
6
+ * Path-prefix containment check. Both inputs must be absolute.
7
+ * Handles the `/foo/bar-evil` vs `/foo/bar/` aliasing gotcha,
8
+ * normalising the parent with a trailing slash before `startsWith`.
9
+ */
10
+ export function isUnder(candidate, parent) {
11
+ const normalisedParent = parent.endsWith('/') ? parent : `${parent}/`;
12
+ return candidate === parent || candidate.startsWith(normalisedParent);
13
+ }
14
+ /** `true` when `path` resolves to anything (file, dir, symlink). */
15
+ export async function pathExists(path) {
16
+ try {
17
+ await stat(path);
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ export function workspaceArtifactsDir(workspaceRoot) {
25
+ return join(workspaceRoot, 'artifacts');
26
+ }
27
+ export function graphJsonPath(workspaceRoot) {
28
+ return join(workspaceArtifactsDir(workspaceRoot), 'model.json');
29
+ }
30
+ export function batchPlanPath(workspaceRoot) {
31
+ return join(workspaceArtifactsDir(workspaceRoot), 'batch-plan.json');
32
+ }
33
+ export function sourceUnitObservationDir(workspaceRoot) {
34
+ return join(workspaceArtifactsDir(workspaceRoot), 'source-unit-state');
35
+ }
36
+ export function sourceUnitObservationFilePath(workspaceRoot, sourceId, relativePath) {
37
+ // Drop the trailing slash from folder units,
38
+ // so the file name doesn't end in `/.json`.
39
+ // In the unit name,
40
+ // replace path separators with `__`, so a nested unit maps to one file.
41
+ const trimmed = relativePath.endsWith('/') ? relativePath.slice(0, -1) : relativePath;
42
+ const flattened = trimmed.replace(/\//g, '__');
43
+ return join(sourceUnitObservationDir(workspaceRoot), sourceId, `${flattened}.json`);
44
+ }
45
+ export function sourceUnitObservationSourceDir(workspaceRoot, sourceId) {
46
+ return join(sourceUnitObservationDir(workspaceRoot), sourceId);
47
+ }
48
+ export function proposalsDir(workspaceRoot, status) {
49
+ return join(workspaceArtifactsDir(workspaceRoot), 'proposals', status);
50
+ }
51
+ export function clarificationDir(workspaceRoot, status) {
52
+ return join(workspaceArtifactsDir(workspaceRoot), 'clarifications', status);
53
+ }
54
+ export function viewsDir(workspaceRoot, kind) {
55
+ return join(workspaceArtifactsDir(workspaceRoot), 'views', kind);
56
+ }
57
+ export function runsDir(workspaceRoot) {
58
+ return join(workspaceArtifactsDir(workspaceRoot), 'runs');
59
+ }
60
+ export function reactorCyclesDir(workspaceRoot) {
61
+ return join(workspaceArtifactsDir(workspaceRoot), 'reactor-cycles');
62
+ }
63
+ export function reactorCycleFilePath(workspaceRoot, cycleId) {
64
+ return join(reactorCyclesDir(workspaceRoot), `${cycleId}.json`);
65
+ }
66
+ export function runIndexPath(workspaceRoot) {
67
+ return join(runsDir(workspaceRoot), 'index.jsonl');
68
+ }
69
+ export function runEventsPath(workspaceRoot, runId) {
70
+ return join(runsDir(workspaceRoot), `${runId}.jsonl`);
71
+ }
72
+ export function runSessionsMetadataPath(workspaceRoot) {
73
+ return join(runsDir(workspaceRoot), 'sessions.jsonl');
74
+ }
75
+ /**
76
+ * claude stores conversation memory keyed by cwd,
77
+ * so resume must spawn from the same dir as the first turn.
78
+ * Keeping it in the workspace, not /tmp, stays derivable from runId,
79
+ * and survives a server restart.
80
+ */
81
+ export function sessionsDir(workspaceRoot) {
82
+ return join(workspaceRoot, '.braid-sessions');
83
+ }
84
+ export function sessionDirPath(workspaceRoot, runId) {
85
+ return join(sessionsDir(workspaceRoot), runId);
86
+ }
87
+ export function workspaceSkillsDir(workspaceRoot) {
88
+ return join(workspaceRoot, 'skills');
89
+ }
90
+ export function workspaceSkillExtensionsDir(workspaceRoot) {
91
+ return join(workspaceRoot, 'skill-extensions');
92
+ }
93
+ export function workspaceProductManifestPath(workspaceRoot) {
94
+ return join(workspaceRoot, 'PRODUCT.md');
95
+ }
96
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/infrastructure/_shared/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ declare const Invite: z.ZodObject<{
3
+ email: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
4
+ invitedAt: z.ZodString;
5
+ serverRole: z.ZodDefault<z.ZodEnum<{
6
+ user: "user";
7
+ admin: "admin";
8
+ }>>;
9
+ }, z.core.$strip>;
10
+ export type Invite = z.infer<typeof Invite>;
11
+ export interface AccessDecision {
12
+ readonly allow: boolean;
13
+ readonly reason?: string;
14
+ readonly viaInvite?: Invite;
15
+ }
16
+ export interface AccessPolicyConfig {
17
+ readonly allowedDomains?: readonly string[];
18
+ readonly allowedEmails?: readonly string[];
19
+ readonly adminEmails?: readonly string[];
20
+ }
21
+ /**
22
+ * Gate 1 decides whether a Google account may log in to this Braid server.
23
+ * It composes two layers.
24
+ *
25
+ * The static allowlist reads the `BRAID_ALLOWED_DOMAINS` and `BRAID_ALLOWED_EMAILS` env vars.
26
+ * Domain matches (`@kdanmobile.com`) and email matches (`alice@example.com`) auto-pass.
27
+ * Both may be left unset on a public instance,
28
+ * combined with an empty invite list this rejects everyone (fail closed).
29
+ *
30
+ * The invite list lives at `${BRAID_HOME}/access.json`, maintained by an admin via `/admin/invites`.
31
+ * One invite per email, consumed on first successful login.
32
+ *
33
+ * The policy is open for either path, a domain pass OR an invite present.
34
+ * That matches the "hybrid" Gate 1 the design picked.
35
+ */
36
+ export declare class AccessPolicy {
37
+ private readonly invitesPath;
38
+ private readonly allowedDomains;
39
+ private readonly allowedEmails;
40
+ private readonly adminEmails;
41
+ constructor(invitesPath: string, config?: AccessPolicyConfig);
42
+ /**
43
+ * Evaluate against the current allowlist and invite file.
44
+ * Reads the invite file every call, since invite management is low-frequency,
45
+ * and a stale cache would create a frustrating race,
46
+ * where an admin invites Bob, Bob logs in, and the server still says "no".
47
+ */
48
+ decide(email: string): Promise<AccessDecision>;
49
+ /**
50
+ * Whether the email should be marked `serverRole='admin'` on the resulting user record.
51
+ * Independent of `decide`, since invites carry their own role override,
52
+ * and an admin email might take the allowlist path with no invite.
53
+ */
54
+ isAdmin(email: string): boolean;
55
+ listInvites(): Promise<Invite[]>;
56
+ addInvite(input: {
57
+ email: string;
58
+ serverRole?: 'admin' | 'user';
59
+ }): Promise<Invite>;
60
+ removeInvite(email: string): Promise<void>;
61
+ /**
62
+ * Consume an invite once the user record exists.
63
+ * Remove it from invites,
64
+ * add to approvedEmails so the user can sign in again without the admin re-inviting them.
65
+ * Idempotent.
66
+ */
67
+ consumeInvite(email: string): Promise<void>;
68
+ /**
69
+ * Add an email to the persistent allowlist directly, without going through the invite step.
70
+ * Used by the bootstrap migration that back-fills approvedEmails,
71
+ * for users who registered before this field existed.
72
+ */
73
+ approveEmail(email: string): Promise<void>;
74
+ /**
75
+ * Drop an email from the persistent allowlist.
76
+ * Called when an admin deletes a user,
77
+ * so the underlying email can't sneak back in without a fresh invite.
78
+ * Idempotent.
79
+ */
80
+ revokeApproval(email: string): Promise<void>;
81
+ listApprovedEmails(): Promise<string[]>;
82
+ private readInvites;
83
+ private readContent;
84
+ private writeContent;
85
+ }
86
+ export {};
87
+ //# sourceMappingURL=AccessPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessPolicy.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/auth/AccessPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,QAAA,MAAM,MAAM;;;;;;;iBAMV,CAAA;AACF,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAA;AAc3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IAIvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IAGxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAEjC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAE3C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAE1C,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACzC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAL9B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAG9B,WAAW,EAAE,MAAM,EACpC,MAAM,GAAE,kBAAuB;IAOjC;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAsBpD;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIzB,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIhC,SAAS,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAenF,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD;;;;;OAKG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjD;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhD;;;;;OAKG;IACG,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS5C,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAI/B,WAAW;YAIX,WAAW;YAeX,YAAY;CAI3B"}
@@ -0,0 +1,177 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { ValidationError } from '@braidhq/core';
4
+ import { z } from 'zod';
5
+ const Invite = z.object({
6
+ email: z.string().email().transform(s => s.toLowerCase()),
7
+ invitedAt: z.string().datetime({ offset: true }),
8
+ // Initial server role applied when the invite is redeemed.
9
+ // Defaults to `user`, so an admin who picks `admin` is making a deliberate choice.
10
+ serverRole: z.enum(['admin', 'user']).default('user'),
11
+ });
12
+ const Content = z.object({
13
+ invites: z.array(Invite).default([]),
14
+ // Emails that have already redeemed an invite (or been approved directly),
15
+ // and are persistently allowed to sign in.
16
+ // Without this, an invite would be a one-shot ticket,
17
+ // gone after the first login, locking the user out on the second.
18
+ // Treat this as the "dynamic allowlist" that grows as invites get consumed,
19
+ // revoking a user removes their entry here.
20
+ approvedEmails: z.array(z.string().email().transform(s => s.toLowerCase())).default([]),
21
+ });
22
+ /**
23
+ * Gate 1 decides whether a Google account may log in to this Braid server.
24
+ * It composes two layers.
25
+ *
26
+ * The static allowlist reads the `BRAID_ALLOWED_DOMAINS` and `BRAID_ALLOWED_EMAILS` env vars.
27
+ * Domain matches (`@kdanmobile.com`) and email matches (`alice@example.com`) auto-pass.
28
+ * Both may be left unset on a public instance,
29
+ * combined with an empty invite list this rejects everyone (fail closed).
30
+ *
31
+ * The invite list lives at `${BRAID_HOME}/access.json`, maintained by an admin via `/admin/invites`.
32
+ * One invite per email, consumed on first successful login.
33
+ *
34
+ * The policy is open for either path, a domain pass OR an invite present.
35
+ * That matches the "hybrid" Gate 1 the design picked.
36
+ */
37
+ export class AccessPolicy {
38
+ invitesPath;
39
+ allowedDomains;
40
+ allowedEmails;
41
+ adminEmails;
42
+ constructor(invitesPath, config = {}) {
43
+ this.invitesPath = invitesPath;
44
+ this.allowedDomains = new Set((config.allowedDomains ?? []).map(s => s.toLowerCase()));
45
+ this.allowedEmails = new Set((config.allowedEmails ?? []).map(s => s.toLowerCase()));
46
+ this.adminEmails = new Set((config.adminEmails ?? []).map(s => s.toLowerCase()));
47
+ }
48
+ /**
49
+ * Evaluate against the current allowlist and invite file.
50
+ * Reads the invite file every call, since invite management is low-frequency,
51
+ * and a stale cache would create a frustrating race,
52
+ * where an admin invites Bob, Bob logs in, and the server still says "no".
53
+ */
54
+ async decide(email) {
55
+ const lower = email.toLowerCase();
56
+ if (this.allowedEmails.has(lower))
57
+ return { allow: true };
58
+ const domain = lower.split('@')[1] ?? '';
59
+ if (domain && this.allowedDomains.has(domain))
60
+ return { allow: true };
61
+ const content = await this.readContent();
62
+ // Approved (post-redemption) allowlist persists across logins.
63
+ if (content.approvedEmails.includes(lower))
64
+ return { allow: true };
65
+ const invite = content.invites.find(i => i.email === lower);
66
+ if (invite)
67
+ return { allow: true, viaInvite: invite };
68
+ return {
69
+ allow: false,
70
+ reason: 'This Google account is not authorized to sign in. Ask an admin to invite you.',
71
+ };
72
+ }
73
+ /**
74
+ * Whether the email should be marked `serverRole='admin'` on the resulting user record.
75
+ * Independent of `decide`, since invites carry their own role override,
76
+ * and an admin email might take the allowlist path with no invite.
77
+ */
78
+ isAdmin(email) {
79
+ return this.adminEmails.has(email.toLowerCase());
80
+ }
81
+ async listInvites() {
82
+ return this.readInvites();
83
+ }
84
+ async addInvite(input) {
85
+ const lower = input.email.toLowerCase();
86
+ const content = await this.readContent();
87
+ if (content.invites.some(i => i.email === lower))
88
+ throw new ValidationError(`Invite for "${lower}" already exists`);
89
+ const invite = {
90
+ email: lower,
91
+ invitedAt: new Date().toISOString(),
92
+ serverRole: input.serverRole ?? 'user',
93
+ };
94
+ content.invites.push(invite);
95
+ await this.writeContent(content);
96
+ return invite;
97
+ }
98
+ async removeInvite(email) {
99
+ const lower = email.toLowerCase();
100
+ const content = await this.readContent();
101
+ const filtered = content.invites.filter(i => i.email !== lower);
102
+ if (filtered.length === content.invites.length)
103
+ return;
104
+ await this.writeContent({ ...content, invites: filtered });
105
+ }
106
+ /**
107
+ * Consume an invite once the user record exists.
108
+ * Remove it from invites,
109
+ * add to approvedEmails so the user can sign in again without the admin re-inviting them.
110
+ * Idempotent.
111
+ */
112
+ async consumeInvite(email) {
113
+ const lower = email.toLowerCase();
114
+ const content = await this.readContent();
115
+ const invites = content.invites.filter(i => i.email !== lower);
116
+ const approvedEmails = content.approvedEmails.includes(lower)
117
+ ? content.approvedEmails
118
+ : [...content.approvedEmails, lower];
119
+ if (invites.length === content.invites.length && approvedEmails === content.approvedEmails)
120
+ return;
121
+ await this.writeContent({ invites, approvedEmails });
122
+ }
123
+ /**
124
+ * Add an email to the persistent allowlist directly, without going through the invite step.
125
+ * Used by the bootstrap migration that back-fills approvedEmails,
126
+ * for users who registered before this field existed.
127
+ */
128
+ async approveEmail(email) {
129
+ const lower = email.toLowerCase();
130
+ const content = await this.readContent();
131
+ if (content.approvedEmails.includes(lower))
132
+ return;
133
+ await this.writeContent({
134
+ ...content,
135
+ approvedEmails: [...content.approvedEmails, lower],
136
+ });
137
+ }
138
+ /**
139
+ * Drop an email from the persistent allowlist.
140
+ * Called when an admin deletes a user,
141
+ * so the underlying email can't sneak back in without a fresh invite.
142
+ * Idempotent.
143
+ */
144
+ async revokeApproval(email) {
145
+ const lower = email.toLowerCase();
146
+ const content = await this.readContent();
147
+ const filtered = content.approvedEmails.filter(e => e !== lower);
148
+ if (filtered.length === content.approvedEmails.length)
149
+ return;
150
+ await this.writeContent({ ...content, approvedEmails: filtered });
151
+ }
152
+ async listApprovedEmails() {
153
+ return (await this.readContent()).approvedEmails;
154
+ }
155
+ async readInvites() {
156
+ return (await this.readContent()).invites;
157
+ }
158
+ async readContent() {
159
+ try {
160
+ const raw = await readFile(this.invitesPath, 'utf-8');
161
+ const parsed = Content.safeParse(JSON.parse(raw));
162
+ if (!parsed.success)
163
+ throw new ValidationError(`Invalid access file at "${this.invitesPath}": ${parsed.error.message}`);
164
+ return parsed.data;
165
+ }
166
+ catch (error) {
167
+ if (error.code === 'ENOENT')
168
+ return { invites: [], approvedEmails: [] };
169
+ throw error;
170
+ }
171
+ }
172
+ async writeContent(content) {
173
+ await mkdir(dirname(this.invitesPath), { recursive: true });
174
+ await writeFile(this.invitesPath, `${JSON.stringify(content, null, 2)}\n`, 'utf-8');
175
+ }
176
+ }
177
+ //# sourceMappingURL=AccessPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessPolicy.js","sourceRoot":"","sources":["../../../src/infrastructure/auth/AccessPolicy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { UserId } from '@braidhq/schema';
2
+ export interface IssuedSession {
3
+ readonly token: string;
4
+ readonly userId: UserId;
5
+ readonly expiresAt?: string;
6
+ }
7
+ export interface ResolvedSession {
8
+ readonly userId: UserId;
9
+ readonly expiresAt?: string;
10
+ }
11
+ /**
12
+ * The port. A hosted deployment swaps the file-backed impl for a shared store,
13
+ * such as Redis or Postgres, behind this interface.
14
+ */
15
+ export interface SessionStore {
16
+ issue: (userId: UserId, options?: {
17
+ ttlSeconds?: number;
18
+ }) => Promise<IssuedSession>;
19
+ resolve: (token: string) => Promise<ResolvedSession | null>;
20
+ revoke: (token: string) => Promise<void>;
21
+ revokeAllForUser: (userId: UserId) => Promise<void>;
22
+ }
23
+ /**
24
+ * Hashed session tokens persisted to `${BRAID_HOME}/sessions.json`.
25
+ * Token format is 32 random bytes encoded as `base64url` (43 chars).
26
+ * The plain text is returned once at issuance time, the server stores SHA-256 of it.
27
+ * A leaked sessions.json therefore cannot be replayed as a Bearer header.
28
+ *
29
+ * No rotation, no rolling expiry, no per-device tracking,
30
+ * this is the minimum useful surface for v0.2.
31
+ * Future remote-server work can revisit if needed.
32
+ */
33
+ export declare class FsSessionStore implements SessionStore {
34
+ private readonly filePath;
35
+ constructor(filePath: string);
36
+ issue(userId: UserId, options?: {
37
+ ttlSeconds?: number;
38
+ }): Promise<IssuedSession>;
39
+ /**
40
+ * Resolve a Bearer token to its session.
41
+ * Updates `lastUsedAt` as a side effect,
42
+ * not required for correctness but useful for the future "active sessions" admin view.
43
+ */
44
+ resolve(token: string): Promise<ResolvedSession | null>;
45
+ revoke(token: string): Promise<void>;
46
+ revokeAllForUser(userId: UserId): Promise<void>;
47
+ private read;
48
+ private write;
49
+ }
50
+ //# sourceMappingURL=SessionStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionStore.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/auth/SessionStore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AA2B7C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;IACpF,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;IAC3D,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAe,YAAW,YAAY;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAEvC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAa1F;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAevD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASpC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAQvC,IAAI;YAeJ,KAAK;CAIpB"}
@@ -0,0 +1,104 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname } from 'node:path';
4
+ import { ValidationError } from '@braidhq/core';
5
+ import { z } from 'zod';
6
+ const SessionRow = z.object({
7
+ // SHA-256 hex of the actual token.
8
+ // The plaintext is shown to the client once at issuance time,
9
+ // the server only ever sees the hash thereafter.
10
+ // Constant-time compare is unnecessary for a pre-image lookup on hex strings,
11
+ // which is what `findByToken` does.
12
+ tokenHash: z.string().length(64),
13
+ userId: z.string().min(1),
14
+ createdAt: z.string().datetime({ offset: true }),
15
+ expiresAt: z.string().datetime({ offset: true }).optional(),
16
+ lastUsedAt: z.string().datetime({ offset: true }).optional(),
17
+ });
18
+ const StoreContent = z.object({
19
+ sessions: z.array(SessionRow).default([]),
20
+ });
21
+ /**
22
+ * Hashed session tokens persisted to `${BRAID_HOME}/sessions.json`.
23
+ * Token format is 32 random bytes encoded as `base64url` (43 chars).
24
+ * The plain text is returned once at issuance time, the server stores SHA-256 of it.
25
+ * A leaked sessions.json therefore cannot be replayed as a Bearer header.
26
+ *
27
+ * No rotation, no rolling expiry, no per-device tracking,
28
+ * this is the minimum useful surface for v0.2.
29
+ * Future remote-server work can revisit if needed.
30
+ */
31
+ export class FsSessionStore {
32
+ filePath;
33
+ constructor(filePath) {
34
+ this.filePath = filePath;
35
+ }
36
+ async issue(userId, options = {}) {
37
+ const content = await this.read();
38
+ const token = randomBytes(32).toString('base64url');
39
+ const tokenHash = sha256Hex(token);
40
+ const createdAt = new Date().toISOString();
41
+ const expiresAt = options.ttlSeconds
42
+ ? new Date(Date.now() + options.ttlSeconds * 1000).toISOString()
43
+ : undefined;
44
+ content.sessions.push({ tokenHash, userId, createdAt, ...(expiresAt ? { expiresAt } : {}) });
45
+ await this.write(content);
46
+ return { token, userId, ...(expiresAt ? { expiresAt } : {}) };
47
+ }
48
+ /**
49
+ * Resolve a Bearer token to its session.
50
+ * Updates `lastUsedAt` as a side effect,
51
+ * not required for correctness but useful for the future "active sessions" admin view.
52
+ */
53
+ async resolve(token) {
54
+ if (!token || token.length === 0)
55
+ return null;
56
+ const tokenHash = sha256Hex(token);
57
+ const content = await this.read();
58
+ const row = content.sessions.find(s => s.tokenHash === tokenHash);
59
+ if (!row)
60
+ return null;
61
+ if (row.expiresAt && Date.parse(row.expiresAt) < Date.now())
62
+ return null;
63
+ row.lastUsedAt = new Date().toISOString();
64
+ await this.write(content);
65
+ return { userId: row.userId, ...(row.expiresAt ? { expiresAt: row.expiresAt } : {}) };
66
+ }
67
+ async revoke(token) {
68
+ const tokenHash = sha256Hex(token);
69
+ const content = await this.read();
70
+ const filtered = content.sessions.filter(s => s.tokenHash !== tokenHash);
71
+ if (filtered.length === content.sessions.length)
72
+ return;
73
+ await this.write({ sessions: filtered });
74
+ }
75
+ async revokeAllForUser(userId) {
76
+ const content = await this.read();
77
+ const filtered = content.sessions.filter(s => s.userId !== userId);
78
+ if (filtered.length === content.sessions.length)
79
+ return;
80
+ await this.write({ sessions: filtered });
81
+ }
82
+ async read() {
83
+ try {
84
+ const raw = await readFile(this.filePath, 'utf-8');
85
+ const parsed = StoreContent.safeParse(JSON.parse(raw));
86
+ if (!parsed.success)
87
+ throw new ValidationError(`Invalid session store at "${this.filePath}": ${parsed.error.message}`);
88
+ return parsed.data;
89
+ }
90
+ catch (error) {
91
+ if (error.code === 'ENOENT')
92
+ return { sessions: [] };
93
+ throw error;
94
+ }
95
+ }
96
+ async write(content) {
97
+ await mkdir(dirname(this.filePath), { recursive: true });
98
+ await writeFile(this.filePath, `${JSON.stringify(content, null, 2)}\n`, 'utf-8');
99
+ }
100
+ }
101
+ function sha256Hex(input) {
102
+ return createHash('sha256').update(input).digest('hex');
103
+ }
104
+ //# sourceMappingURL=SessionStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionStore.js","sourceRoot":"","sources":["../../../src/infrastructure/auth/SessionStore.ts"],"names":[],"mappings":"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"}