@openlup/platform 0.0.0 → 0.16.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 (395) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +204 -0
  3. package/README.md +471 -1
  4. package/dist/bff/dispatch.d.ts +27 -0
  5. package/dist/bff/dispatch.js +47 -0
  6. package/dist/bff/envelope.d.ts +46 -0
  7. package/dist/bff/envelope.js +80 -0
  8. package/dist/bff/health.d.ts +24 -0
  9. package/dist/bff/health.js +19 -0
  10. package/dist/bff/index.d.ts +6 -0
  11. package/dist/bff/index.js +6 -0
  12. package/dist/bff/lazyRoute.d.ts +4 -0
  13. package/dist/bff/lazyRoute.js +13 -0
  14. package/dist/bff/requestContext.d.ts +17 -0
  15. package/dist/bff/requestContext.js +26 -0
  16. package/dist/bff/routerRewrite.d.ts +2 -0
  17. package/dist/bff/routerRewrite.js +31 -0
  18. package/dist/blob/binding.d.ts +16 -0
  19. package/dist/blob/binding.js +37 -0
  20. package/dist/blob/filesystem.d.ts +8 -0
  21. package/dist/blob/filesystem.js +71 -0
  22. package/dist/blob/index.d.ts +6 -0
  23. package/dist/blob/index.js +6 -0
  24. package/dist/blob/s3.d.ts +32 -0
  25. package/dist/blob/s3.js +56 -0
  26. package/dist/blob/s3Client.d.ts +25 -0
  27. package/dist/blob/s3Client.js +76 -0
  28. package/dist/blob/supabase.d.ts +40 -0
  29. package/dist/blob/supabase.js +59 -0
  30. package/dist/blob/vercel.d.ts +63 -0
  31. package/dist/blob/vercel.js +69 -0
  32. package/dist/composition/composeBundle.d.ts +47 -0
  33. package/dist/composition/composeBundle.js +45 -0
  34. package/dist/composition/httpBinding.d.ts +8 -0
  35. package/dist/composition/httpBinding.js +18 -0
  36. package/dist/composition/index.d.ts +3 -0
  37. package/dist/composition/index.js +3 -0
  38. package/dist/composition/nodeHost.d.ts +103 -0
  39. package/dist/composition/nodeHost.js +145 -0
  40. package/dist/data/binding.d.ts +34 -0
  41. package/dist/data/binding.js +92 -0
  42. package/dist/data/index.d.ts +5 -0
  43. package/dist/data/index.js +5 -0
  44. package/dist/data/postgresGateway.d.ts +42 -0
  45. package/dist/data/postgresGateway.js +135 -0
  46. package/dist/data/queryBuilder.d.ts +54 -0
  47. package/dist/data/queryBuilder.js +240 -0
  48. package/dist/data/queryBuilderSql.d.ts +76 -0
  49. package/dist/data/queryBuilderSql.js +145 -0
  50. package/dist/data/supabaseClientFactory.d.ts +34 -0
  51. package/dist/data/supabaseClientFactory.js +56 -0
  52. package/dist/data/supabaseGateway.d.ts +22 -0
  53. package/dist/data/supabaseGateway.js +49 -0
  54. package/dist/http/deadline.d.ts +20 -0
  55. package/dist/http/deadline.js +93 -0
  56. package/dist/http/index.d.ts +6 -0
  57. package/dist/http/index.js +5 -0
  58. package/dist/http/requestAdapter.d.ts +38 -0
  59. package/dist/http/requestAdapter.js +203 -0
  60. package/dist/http/runtime.d.ts +83 -0
  61. package/dist/http/runtime.js +199 -0
  62. package/dist/http/security.d.ts +44 -0
  63. package/dist/http/security.js +114 -0
  64. package/dist/http/staticAssets.d.ts +3 -0
  65. package/dist/http/staticAssets.js +42 -0
  66. package/dist/http/types.d.ts +23 -0
  67. package/dist/http/types.js +6 -0
  68. package/dist/identity/adminAuthBinding.d.ts +28 -0
  69. package/dist/identity/adminAuthBinding.js +66 -0
  70. package/dist/identity/adminAuthorization.d.ts +34 -0
  71. package/dist/identity/adminAuthorization.js +72 -0
  72. package/dist/identity/adminContracts.d.ts +70 -0
  73. package/dist/identity/adminContracts.js +43 -0
  74. package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
  75. package/dist/identity/adminMagicLinkHandler.js +94 -0
  76. package/dist/identity/adminMeHandler.d.ts +15 -0
  77. package/dist/identity/adminMeHandler.js +49 -0
  78. package/dist/identity/adminPreflight.d.ts +16 -0
  79. package/dist/identity/adminPreflight.js +38 -0
  80. package/dist/identity/adminUserActionPorts.d.ts +31 -0
  81. package/dist/identity/adminUserActionPorts.js +51 -0
  82. package/dist/identity/adminUserHandlers.d.ts +38 -0
  83. package/dist/identity/adminUserHandlers.js +140 -0
  84. package/dist/identity/authVerifyUrl.d.ts +6 -0
  85. package/dist/identity/authVerifyUrl.js +54 -0
  86. package/dist/identity/contracts/contracts.d.ts +8 -0
  87. package/dist/identity/contracts/contracts.js +2 -0
  88. package/dist/identity/contracts/emailTrust.d.ts +3 -0
  89. package/dist/identity/contracts/emailTrust.js +4 -0
  90. package/dist/identity/contracts/index.d.ts +8 -0
  91. package/dist/identity/contracts/index.js +8 -0
  92. package/dist/identity/contracts/oidc.d.ts +3 -0
  93. package/dist/identity/contracts/oidc.js +9 -0
  94. package/dist/identity/contracts/operatorAuth.d.ts +35 -0
  95. package/dist/identity/contracts/operatorAuth.js +53 -0
  96. package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
  97. package/dist/identity/contracts/operatorInvitations.js +1 -0
  98. package/dist/identity/contracts/ports.d.ts +22 -0
  99. package/dist/identity/contracts/ports.js +1 -0
  100. package/dist/identity/contracts/privacy.d.ts +62 -0
  101. package/dist/identity/contracts/privacy.js +99 -0
  102. package/dist/identity/contracts/providerList.d.ts +3 -0
  103. package/dist/identity/contracts/providerList.js +11 -0
  104. package/dist/identity/contracts/types.d.ts +16 -0
  105. package/dist/identity/contracts/types.js +4 -0
  106. package/dist/identity/contribution.d.ts +8 -0
  107. package/dist/identity/contribution.js +22 -0
  108. package/dist/identity/customerAuth.d.ts +8 -0
  109. package/dist/identity/customerAuth.js +1 -0
  110. package/dist/identity/customerAuthorization.d.ts +5 -0
  111. package/dist/identity/customerAuthorization.js +22 -0
  112. package/dist/identity/customerContracts.d.ts +42 -0
  113. package/dist/identity/customerContracts.js +43 -0
  114. package/dist/identity/customerIdentityBinding.d.ts +44 -0
  115. package/dist/identity/customerIdentityBinding.js +60 -0
  116. package/dist/identity/customerIdentityVerifier.d.ts +8 -0
  117. package/dist/identity/customerIdentityVerifier.js +101 -0
  118. package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
  119. package/dist/identity/customerMagicLinkHandler.js +115 -0
  120. package/dist/identity/customerMeHandler.d.ts +11 -0
  121. package/dist/identity/customerMeHandler.js +46 -0
  122. package/dist/identity/customerPorts.d.ts +4 -0
  123. package/dist/identity/customerPorts.js +1 -0
  124. package/dist/identity/customerSessionBinding.d.ts +38 -0
  125. package/dist/identity/customerSessionBinding.js +80 -0
  126. package/dist/identity/customerSessionIssuer.d.ts +10 -0
  127. package/dist/identity/customerSessionIssuer.js +57 -0
  128. package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
  129. package/dist/identity/customerVerifyOtpHandler.js +41 -0
  130. package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
  131. package/dist/identity/directOperatorInvitationStore.js +193 -0
  132. package/dist/identity/emailTrustPolicy.d.ts +14 -0
  133. package/dist/identity/emailTrustPolicy.js +16 -0
  134. package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
  135. package/dist/identity/identityPersonalDataContributor.js +46 -0
  136. package/dist/identity/identityReadiness.d.ts +10 -0
  137. package/dist/identity/identityReadiness.js +25 -0
  138. package/dist/identity/index.d.ts +67 -0
  139. package/dist/identity/index.js +66 -0
  140. package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
  141. package/dist/identity/inviteAdminUserUseCase.js +75 -0
  142. package/dist/identity/jwtLibrary.d.ts +54 -0
  143. package/dist/identity/jwtLibrary.js +1 -0
  144. package/dist/identity/machineActorWriteFence.d.ts +4 -0
  145. package/dist/identity/machineActorWriteFence.js +13 -0
  146. package/dist/identity/magicLinkPorts.d.ts +28 -0
  147. package/dist/identity/magicLinkPorts.js +1 -0
  148. package/dist/identity/managedAccountLinkStore.d.ts +2 -0
  149. package/dist/identity/managedAccountLinkStore.js +93 -0
  150. package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
  151. package/dist/identity/managedAdminAuthVerifier.js +27 -0
  152. package/dist/identity/managedAdminUserGateways.d.ts +123 -0
  153. package/dist/identity/managedAdminUserGateways.js +127 -0
  154. package/dist/identity/managedClient.d.ts +90 -0
  155. package/dist/identity/managedClient.js +10 -0
  156. package/dist/identity/managedClientAuthLink.d.ts +3 -0
  157. package/dist/identity/managedClientAuthLink.js +115 -0
  158. package/dist/identity/managedCustomerMe.d.ts +13 -0
  159. package/dist/identity/managedCustomerMe.js +30 -0
  160. package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
  161. package/dist/identity/managedEmailTrustPolicy.js +105 -0
  162. package/dist/identity/managedIdentityVerifier.d.ts +2 -0
  163. package/dist/identity/managedIdentityVerifier.js +16 -0
  164. package/dist/identity/managedPrivacy.d.ts +4 -0
  165. package/dist/identity/managedPrivacy.js +39 -0
  166. package/dist/identity/messages.d.ts +29 -0
  167. package/dist/identity/messages.js +39 -0
  168. package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
  169. package/dist/identity/oidcIdentityVerifier.js +309 -0
  170. package/dist/identity/oidcPrincipal.d.ts +2 -0
  171. package/dist/identity/oidcPrincipal.js +10 -0
  172. package/dist/identity/operatorAdminAuth.d.ts +6 -0
  173. package/dist/identity/operatorAdminAuth.js +13 -0
  174. package/dist/identity/operatorAuthHandlers.d.ts +18 -0
  175. package/dist/identity/operatorAuthHandlers.js +71 -0
  176. package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
  177. package/dist/identity/operatorIdentityVerifier.js +19 -0
  178. package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
  179. package/dist/identity/operatorInvitationLifecycle.js +15 -0
  180. package/dist/identity/operatorInvitationPorts.d.ts +31 -0
  181. package/dist/identity/operatorInvitationPorts.js +1 -0
  182. package/dist/identity/operatorPorts.d.ts +37 -0
  183. package/dist/identity/operatorPorts.js +1 -0
  184. package/dist/identity/operatorSession.d.ts +13 -0
  185. package/dist/identity/operatorSession.js +31 -0
  186. package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
  187. package/dist/identity/operatorTokenAdminAuth.js +50 -0
  188. package/dist/identity/personalDataRegistry.d.ts +4 -0
  189. package/dist/identity/personalDataRegistry.js +11 -0
  190. package/dist/identity/platformAdminAuth.d.ts +15 -0
  191. package/dist/identity/platformAdminAuth.js +1 -0
  192. package/dist/identity/ports.d.ts +127 -0
  193. package/dist/identity/ports.js +38 -0
  194. package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
  195. package/dist/identity/postgresAccountLinkStore.js +52 -0
  196. package/dist/identity/postgresCustomerMe.d.ts +3 -0
  197. package/dist/identity/postgresCustomerMe.js +21 -0
  198. package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
  199. package/dist/identity/postgresCustomerRecovery.js +57 -0
  200. package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
  201. package/dist/identity/postgresOperatorIdentity.js +137 -0
  202. package/dist/identity/postgresPrivacy.d.ts +7 -0
  203. package/dist/identity/postgresPrivacy.js +126 -0
  204. package/dist/identity/privacyHandlers.d.ts +10 -0
  205. package/dist/identity/privacyHandlers.js +35 -0
  206. package/dist/identity/privacyPorts.d.ts +72 -0
  207. package/dist/identity/privacyPorts.js +8 -0
  208. package/dist/identity/privacyUseCases.d.ts +5 -0
  209. package/dist/identity/privacyUseCases.js +55 -0
  210. package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
  211. package/dist/identity/reconcileAccountForPrincipal.js +66 -0
  212. package/dist/identity/reconcileAccountHandler.d.ts +11 -0
  213. package/dist/identity/reconcileAccountHandler.js +30 -0
  214. package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
  215. package/dist/identity/removeAdminUserUseCase.js +60 -0
  216. package/dist/identity/response.d.ts +3 -0
  217. package/dist/identity/response.js +2 -0
  218. package/dist/identity/sql.d.ts +44 -0
  219. package/dist/identity/sql.js +10 -0
  220. package/dist/job-ledger/index.d.ts +5 -0
  221. package/dist/job-ledger/index.js +3 -0
  222. package/dist/job-ledger/ports.d.ts +10 -0
  223. package/dist/job-ledger/ports.js +7 -0
  224. package/dist/job-ledger/postgres.d.ts +10 -0
  225. package/dist/job-ledger/postgres.js +101 -0
  226. package/dist/job-ledger/sql.d.ts +7 -0
  227. package/dist/job-ledger/sql.js +10 -0
  228. package/dist/job-ledger/supabase.d.ts +77 -0
  229. package/dist/job-ledger/supabase.js +202 -0
  230. package/dist/kernel/index.d.ts +1 -0
  231. package/dist/kernel/index.js +1 -0
  232. package/dist/kernel/platformKernel.d.ts +34 -0
  233. package/dist/kernel/platformKernel.js +115 -0
  234. package/dist/migrations/index.d.ts +1 -0
  235. package/dist/migrations/index.js +1 -0
  236. package/dist/migrations/postgresRunner.d.ts +50 -0
  237. package/dist/migrations/postgresRunner.js +176 -0
  238. package/dist/scheduler/advisoryLock.d.ts +44 -0
  239. package/dist/scheduler/advisoryLock.js +114 -0
  240. package/dist/scheduler/cronGuard.d.ts +10 -0
  241. package/dist/scheduler/cronGuard.js +19 -0
  242. package/dist/scheduler/cronSecret.d.ts +5 -0
  243. package/dist/scheduler/cronSecret.js +23 -0
  244. package/dist/scheduler/handlers.d.ts +9 -0
  245. package/dist/scheduler/handlers.js +10 -0
  246. package/dist/scheduler/httpEntry.d.ts +16 -0
  247. package/dist/scheduler/httpEntry.js +31 -0
  248. package/dist/scheduler/index.d.ts +9 -0
  249. package/dist/scheduler/index.js +9 -0
  250. package/dist/scheduler/jobRegistry.d.ts +21 -0
  251. package/dist/scheduler/jobRegistry.js +39 -0
  252. package/dist/scheduler/jobs.d.ts +48 -0
  253. package/dist/scheduler/jobs.js +92 -0
  254. package/dist/scheduler/nodeScheduler.d.ts +42 -0
  255. package/dist/scheduler/nodeScheduler.js +73 -0
  256. package/dist/scheduler/vercelScheduler.d.ts +29 -0
  257. package/dist/scheduler/vercelScheduler.js +51 -0
  258. package/package.json +116 -3
  259. package/release-gates.json +143 -0
  260. package/sql/20260809150000_job_run_ledger.sql +301 -0
  261. package/sql/20260809150001_job_run_ledger_v3.sql +70 -0
  262. package/sql/20260810180001_self_host_actor_auth.sql +40 -0
  263. package/sql/20260810204057_customer_identity_read.sql +31 -0
  264. package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
  265. package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
  266. package/sql/20261018120000_identity_operator_invitations.sql +42 -0
  267. package/sql/20261018180000_identity_operator_challenges.sql +56 -0
  268. package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
  269. package/src/bff/dispatch.ts +81 -0
  270. package/src/bff/envelope.ts +142 -0
  271. package/src/bff/health.ts +37 -0
  272. package/src/bff/index.ts +30 -0
  273. package/src/bff/lazyRoute.ts +28 -0
  274. package/src/bff/requestContext.ts +48 -0
  275. package/src/bff/routerRewrite.ts +38 -0
  276. package/src/blob/binding.ts +55 -0
  277. package/src/blob/filesystem.ts +86 -0
  278. package/src/blob/index.ts +17 -0
  279. package/src/blob/s3.ts +87 -0
  280. package/src/blob/s3Client.ts +91 -0
  281. package/src/blob/supabase.ts +88 -0
  282. package/src/blob/vercel.ts +127 -0
  283. package/src/composition/composeBundle.ts +109 -0
  284. package/src/composition/httpBinding.ts +30 -0
  285. package/src/composition/index.ts +17 -0
  286. package/src/composition/nodeHost.ts +289 -0
  287. package/src/data/binding.ts +157 -0
  288. package/src/data/index.ts +33 -0
  289. package/src/data/postgresGateway.ts +186 -0
  290. package/src/data/queryBuilder.ts +300 -0
  291. package/src/data/queryBuilderSql.ts +183 -0
  292. package/src/data/supabaseClientFactory.ts +92 -0
  293. package/src/data/supabaseGateway.ts +76 -0
  294. package/src/http/deadline.ts +117 -0
  295. package/src/http/index.ts +36 -0
  296. package/src/http/requestAdapter.ts +215 -0
  297. package/src/http/runtime.ts +319 -0
  298. package/src/http/security.ts +155 -0
  299. package/src/http/staticAssets.ts +42 -0
  300. package/src/http/types.ts +31 -0
  301. package/src/identity/adminAuthBinding.ts +76 -0
  302. package/src/identity/adminAuthorization.ts +106 -0
  303. package/src/identity/adminContracts.ts +52 -0
  304. package/src/identity/adminMagicLinkHandler.ts +132 -0
  305. package/src/identity/adminMeHandler.ts +64 -0
  306. package/src/identity/adminPreflight.ts +46 -0
  307. package/src/identity/adminUserActionPorts.ts +78 -0
  308. package/src/identity/adminUserHandlers.ts +174 -0
  309. package/src/identity/authVerifyUrl.ts +56 -0
  310. package/src/identity/contracts/contracts.ts +15 -0
  311. package/src/identity/contracts/emailTrust.ts +5 -0
  312. package/src/identity/contracts/index.ts +8 -0
  313. package/src/identity/contracts/oidc.ts +10 -0
  314. package/src/identity/contracts/operatorAuth.ts +61 -0
  315. package/src/identity/contracts/operatorInvitations.ts +32 -0
  316. package/src/identity/contracts/ports.ts +25 -0
  317. package/src/identity/contracts/privacy.ts +86 -0
  318. package/src/identity/contracts/providerList.ts +13 -0
  319. package/src/identity/contracts/types.ts +25 -0
  320. package/src/identity/contribution.ts +29 -0
  321. package/src/identity/customerAuth.ts +3 -0
  322. package/src/identity/customerAuthorization.ts +38 -0
  323. package/src/identity/customerContracts.ts +52 -0
  324. package/src/identity/customerIdentityBinding.ts +125 -0
  325. package/src/identity/customerIdentityVerifier.ts +141 -0
  326. package/src/identity/customerMagicLinkHandler.ts +171 -0
  327. package/src/identity/customerMeHandler.ts +64 -0
  328. package/src/identity/customerPorts.ts +2 -0
  329. package/src/identity/customerSessionBinding.ts +114 -0
  330. package/src/identity/customerSessionIssuer.ts +71 -0
  331. package/src/identity/customerVerifyOtpHandler.ts +44 -0
  332. package/src/identity/directOperatorInvitationStore.ts +178 -0
  333. package/src/identity/emailTrustPolicy.ts +16 -0
  334. package/src/identity/identityPersonalDataContributor.ts +48 -0
  335. package/src/identity/identityReadiness.ts +26 -0
  336. package/src/identity/index.ts +69 -0
  337. package/src/identity/inviteAdminUserUseCase.ts +134 -0
  338. package/src/identity/jwtLibrary.ts +24 -0
  339. package/src/identity/machineActorWriteFence.ts +19 -0
  340. package/src/identity/magicLinkPorts.ts +7 -0
  341. package/src/identity/managedAccountLinkStore.ts +105 -0
  342. package/src/identity/managedAdminAuthVerifier.ts +40 -0
  343. package/src/identity/managedAdminUserGateways.ts +164 -0
  344. package/src/identity/managedClient.ts +58 -0
  345. package/src/identity/managedClientAuthLink.ts +120 -0
  346. package/src/identity/managedCustomerMe.ts +52 -0
  347. package/src/identity/managedEmailTrustPolicy.ts +90 -0
  348. package/src/identity/managedIdentityVerifier.ts +20 -0
  349. package/src/identity/managedPrivacy.ts +37 -0
  350. package/src/identity/messages.ts +41 -0
  351. package/src/identity/oidcIdentityVerifier.ts +229 -0
  352. package/src/identity/oidcPrincipal.ts +10 -0
  353. package/src/identity/operatorAdminAuth.ts +14 -0
  354. package/src/identity/operatorAuthHandlers.ts +52 -0
  355. package/src/identity/operatorIdentityVerifier.ts +19 -0
  356. package/src/identity/operatorInvitationLifecycle.ts +14 -0
  357. package/src/identity/operatorInvitationPorts.ts +11 -0
  358. package/src/identity/operatorPorts.ts +11 -0
  359. package/src/identity/operatorSession.ts +30 -0
  360. package/src/identity/operatorTokenAdminAuth.ts +79 -0
  361. package/src/identity/personalDataRegistry.ts +8 -0
  362. package/src/identity/platformAdminAuth.ts +7 -0
  363. package/src/identity/ports.ts +158 -0
  364. package/src/identity/postgresAccountLinkStore.ts +49 -0
  365. package/src/identity/postgresCustomerMe.ts +33 -0
  366. package/src/identity/postgresCustomerRecovery.ts +72 -0
  367. package/src/identity/postgresOperatorIdentity.ts +123 -0
  368. package/src/identity/postgresPrivacy.ts +117 -0
  369. package/src/identity/privacyHandlers.ts +33 -0
  370. package/src/identity/privacyPorts.ts +52 -0
  371. package/src/identity/privacyUseCases.ts +48 -0
  372. package/src/identity/reconcileAccountForPrincipal.ts +99 -0
  373. package/src/identity/reconcileAccountHandler.ts +36 -0
  374. package/src/identity/removeAdminUserUseCase.ts +85 -0
  375. package/src/identity/response.ts +3 -0
  376. package/src/identity/sql.ts +10 -0
  377. package/src/job-ledger/index.ts +25 -0
  378. package/src/job-ledger/ports.ts +27 -0
  379. package/src/job-ledger/postgres.ts +121 -0
  380. package/src/job-ledger/sql.ts +12 -0
  381. package/src/job-ledger/supabase.ts +294 -0
  382. package/src/kernel/index.ts +17 -0
  383. package/src/kernel/platformKernel.ts +153 -0
  384. package/src/migrations/index.ts +10 -0
  385. package/src/migrations/postgresRunner.ts +255 -0
  386. package/src/scheduler/advisoryLock.ts +145 -0
  387. package/src/scheduler/cronGuard.ts +30 -0
  388. package/src/scheduler/cronSecret.ts +26 -0
  389. package/src/scheduler/handlers.ts +24 -0
  390. package/src/scheduler/httpEntry.ts +49 -0
  391. package/src/scheduler/index.ts +47 -0
  392. package/src/scheduler/jobRegistry.ts +57 -0
  393. package/src/scheduler/jobs.ts +119 -0
  394. package/src/scheduler/nodeScheduler.ts +123 -0
  395. package/src/scheduler/vercelScheduler.ts +71 -0
@@ -0,0 +1,143 @@
1
+ {
2
+ "schemaVersion": 5,
3
+ "kind": "rail",
4
+ "compatibilityPolicies": {
5
+ "internal-candidate": "Development-preview API; no stable compatibility claim."
6
+ },
7
+ "packageSurface": {
8
+ "./http": {
9
+ "role": "rail",
10
+ "maturity": "experimental",
11
+ "compatibilityPolicy": "internal-candidate",
12
+ "packageSmokeEvidence": [
13
+ "test/packageBoundary.test.ts",
14
+ "test/http/runtime.test.ts"
15
+ ],
16
+ "entrypoint": "dist/http/index.d.ts",
17
+ "snapshot": "api/http.api.md"
18
+ },
19
+ "./bff": {
20
+ "role": "rail",
21
+ "maturity": "experimental",
22
+ "compatibilityPolicy": "internal-candidate",
23
+ "packageSmokeEvidence": [
24
+ "test/packageBoundary.test.ts",
25
+ "test/bff/dispatch.test.ts"
26
+ ],
27
+ "entrypoint": "dist/bff/index.d.ts",
28
+ "snapshot": "api/bff.api.md"
29
+ },
30
+ "./scheduler": {
31
+ "role": "rail",
32
+ "maturity": "experimental",
33
+ "compatibilityPolicy": "internal-candidate",
34
+ "packageSmokeEvidence": [
35
+ "test/packageBoundary.test.ts",
36
+ "test/scheduler/jobs.test.ts"
37
+ ],
38
+ "entrypoint": "dist/scheduler/index.d.ts",
39
+ "snapshot": "api/scheduler.api.md"
40
+ },
41
+ "./blob": {
42
+ "role": "rail",
43
+ "maturity": "experimental",
44
+ "compatibilityPolicy": "internal-candidate",
45
+ "packageSmokeEvidence": [
46
+ "test/packageBoundary.test.ts",
47
+ "test/blob/binding.test.ts"
48
+ ],
49
+ "entrypoint": "dist/blob/index.d.ts",
50
+ "snapshot": "api/blob.api.md"
51
+ },
52
+ "./data": {
53
+ "role": "rail",
54
+ "maturity": "experimental",
55
+ "compatibilityPolicy": "internal-candidate",
56
+ "packageSmokeEvidence": [
57
+ "test/packageBoundary.test.ts",
58
+ "test/data/binding.test.ts"
59
+ ],
60
+ "entrypoint": "dist/data/index.d.ts",
61
+ "snapshot": "api/data.api.md"
62
+ },
63
+ "./migrations": {
64
+ "role": "rail",
65
+ "maturity": "experimental",
66
+ "compatibilityPolicy": "internal-candidate",
67
+ "packageSmokeEvidence": [
68
+ "test/packageBoundary.test.ts",
69
+ "test/migrations/postgresRunner.test.ts"
70
+ ],
71
+ "entrypoint": "dist/migrations/index.d.ts",
72
+ "snapshot": "api/migrations.api.md"
73
+ },
74
+ "./job-ledger": {
75
+ "role": "rail",
76
+ "maturity": "experimental",
77
+ "compatibilityPolicy": "internal-candidate",
78
+ "packageSmokeEvidence": [
79
+ "test/packageBoundary.test.ts",
80
+ "test/job-ledger/postgres.test.ts"
81
+ ],
82
+ "entrypoint": "dist/job-ledger/index.d.ts",
83
+ "snapshot": "api/job-ledger.api.md"
84
+ },
85
+ "./kernel": {
86
+ "role": "rail",
87
+ "maturity": "experimental",
88
+ "compatibilityPolicy": "internal-candidate",
89
+ "packageSmokeEvidence": [
90
+ "test/packageBoundary.test.ts",
91
+ "test/kernel/platformKernel.test.ts"
92
+ ],
93
+ "entrypoint": "dist/kernel/index.d.ts",
94
+ "snapshot": "api/kernel.api.md"
95
+ },
96
+ "./composition": {
97
+ "role": "rail",
98
+ "maturity": "experimental",
99
+ "compatibilityPolicy": "internal-candidate",
100
+ "packageSmokeEvidence": [
101
+ "test/packageBoundary.test.ts",
102
+ "test/composition/composeBundle.test.ts"
103
+ ],
104
+ "entrypoint": "dist/composition/index.d.ts",
105
+ "snapshot": "api/composition.api.md"
106
+ },
107
+ "./identity": {
108
+ "role": "capability",
109
+ "maturity": "experimental",
110
+ "compatibilityPolicy": "internal-candidate",
111
+ "packageSmokeEvidence": [
112
+ "test/packageBoundary.test.ts",
113
+ "test/identity/adminConformance.test.ts"
114
+ ],
115
+ "entrypoint": "dist/identity/index.d.ts",
116
+ "snapshot": "api/identity.api.md"
117
+ },
118
+ "./identity/contracts": {
119
+ "role": "capability",
120
+ "maturity": "experimental",
121
+ "compatibilityPolicy": "internal-candidate",
122
+ "packageSmokeEvidence": [
123
+ "test/packageBoundary.test.ts",
124
+ "test/identity/contracts.test.ts"
125
+ ],
126
+ "entrypoint": "dist/identity/contracts/index.d.ts",
127
+ "snapshot": "api/identity-contracts.api.md"
128
+ }
129
+ },
130
+ "pack": {
131
+ "code": {
132
+ "maxPackedBytes": 131072,
133
+ "maxFiles": 240
134
+ },
135
+ "docs": {
136
+ "maxPackedBytes": 16384,
137
+ "maxFiles": 12
138
+ },
139
+ "maxPackedBytes": 196608,
140
+ "maxUnpackedBytes": 1048576,
141
+ "maxFiles": 256
142
+ }
143
+ }
@@ -0,0 +1,301 @@
1
+ -- Public platform job-run ledger: the two relations a scheduled job needs to be
2
+ -- mutually exclusive across runners, and the two verbs its runtime actually calls.
3
+ --
4
+ -- WHAT THIS FORWARD SHIPS, AND WHY IT IS TWO VERBS AND NOT THREE. The second bundle
5
+ -- already has reads, a queue, a customer mutation, an operator mutation and a shipment
6
+ -- machine. What it has never had is the thing that lets a *scheduled* job run at all:
7
+ -- somewhere to say "this attempt is mine, nobody else start", and somewhere to say
8
+ -- "it is finished, and this is what it did". The consumer of this ledger asks for
9
+ -- exactly those two things -- a claim and a finish -- and for nothing else. There is
10
+ -- no heartbeat, no renew and no extend anywhere in the runtime that drives it, so
11
+ -- there is none here: a lease is issued once, and it is released by finishing or by
12
+ -- expiring. A failure is a finish with status 'failed', not a separate verb.
13
+ --
14
+ -- EXCLUSIVITY IS THE CONTROL ROW, NOT AN ADVISORY LOCK. The claim upserts the job's
15
+ -- control row and then takes `FOR UPDATE` on it. A second claimer arriving in the same
16
+ -- instant blocks on that row rather than racing past it, and once the first claimer
17
+ -- commits it reads a live lease and is refused. This is deliberate and it is the
18
+ -- property the harness proves with two concurrent connections. An advisory lock would
19
+ -- be a different machine: it is not carried by the row, so it would vanish with the
20
+ -- session and leave the ledger claiming a run that nobody holds. The lease, in
21
+ -- contrast, outlives the connection and is what makes a crashed runner recoverable --
22
+ -- after the lease elapses, and not before.
23
+ --
24
+ -- WHAT A FINISH IS ALLOWED TO DO. A finish always stamps its own run row, because a
25
+ -- runner that lost its lease still owes the ledger the truth about what it did. It
26
+ -- releases the *job* only when the control row still carries its own token. So a
27
+ -- zombie -- a runner whose lease expired and whose job was re-claimed by somebody else
28
+ -- -- writes its own epitaph and returns false, and cannot clear a lease it no longer
29
+ -- holds. That returned boolean is the fencing signal, and the harness asserts it.
30
+ --
31
+ -- WHERE THE SEMANTICS COME FROM, AND EVERY PLACE THIS KERNEL DEPARTS FROM THEM. The
32
+ -- behaviour above is derived from the managed chain's own claim/finish pair, read in
33
+ -- full rather than summarised: its two relations and its first begin/finish, the later
34
+ -- migration that added the second finish and the driver/lease/counter columns, the
35
+ -- migration that rewrote the claim into its current 199-line body, and the migration
36
+ -- that introduced a neutral trigger vocabulary. Five departures, each named because a
37
+ -- silent one is how a kernel and its origin quietly stop being the same machine:
38
+ --
39
+ -- 1. THE RUNNER VOCABULARY IS NEUTRAL: 'worker', 'scheduler', 'operator'. The chain's
40
+ -- set enumerates two named cron mechanisms, one of which is the scheduler of a
41
+ -- specific hosting vendor. Which scheduler a deployment happens to run is a fact
42
+ -- about that deployment, never a fact about a platform, and this kernel may not
43
+ -- carry a vendor's name in a CHECK constraint. The vocabulary chosen is not
44
+ -- invented here either: it is the one the chain's own newest control-plane
45
+ -- migration introduced, in its words, so that a deployment adapter is EVIDENCE
46
+ -- rather than an authorization decision. The concrete adapter label survives
47
+ -- losslessly in `p_metadata`, which is caller-supplied jsonb this kernel stores
48
+ -- and never interprets.
49
+ -- 2. THE FIRST CLAIMER DEFINES THE JOB'S ACTIVE RUNNER. Upstream the control row is
50
+ -- created with a fixed default runner, so on a fresh install the first tick of a
51
+ -- long-running worker is refused as the wrong runner until an operator edits a
52
+ -- row by hand. That is a migration artefact of a live cutover, not a platform
53
+ -- rule. Here the auto-created row adopts the runner that created it; changing
54
+ -- runners afterwards stays an explicit operator UPDATE, and the refusal below
55
+ -- still protects a job whose runner has been pinned.
56
+ -- 3. ONE LEASE COLUMN AND ONE COUNTER PAIR. Upstream carries `lease_until` and
57
+ -- `lease_expires_at` for one lease, and `checked`/`checked_count`,
58
+ -- `updated`/`updated_count` for one pair of counts, writing both of each on every
59
+ -- call. Those are compatibility shadows of an earlier signature. This kernel has
60
+ -- no earlier signature, so it ships one of each. For the same reason the run row
61
+ -- records its runner once, where upstream writes the same value into three
62
+ -- columns.
63
+ -- 4. NO `SECURITY DEFINER`, NO `GRANT`/`REVOKE`, NO ROW-LEVEL SECURITY, matching
64
+ -- every forward this catalogue already carries. This kernel creates zero roles.
65
+ -- Said out loud rather than assumed: ON THIS KERNEL THE FUNCTIONS BELOW ARE NOT A
66
+ -- SECURITY BOUNDARY -- the host application is what authenticates, and a caller
67
+ -- that can reach these functions can already reach the tables. `SET search_path`
68
+ -- is kept, because that one is a correctness property and not a role property.
69
+ -- 5. THE FINISH KEEPS ITS `_v2` SUFFIX. There is no v1 in this kernel and the suffix
70
+ -- describes the version history of a different codebase. It is kept anyway: it is
71
+ -- the name the shipped consumer calls, and giving a public kernel a second,
72
+ -- prettier name for one verb would be worse than carrying one inherited one.
73
+ --
74
+ -- WHAT THIS FORWARD DELIBERATELY DOES NOT SHIP, enumerated so nobody has to infer it:
75
+ -- * no runtime binding. Not one line of the application runtime is rewired onto this
76
+ -- ledger by this forward; the files that would do it belong to another line of work
77
+ -- in flight, and the falsifier for this kernel is its own parity harness driving
78
+ -- the shipped port's two calls;
79
+ -- * no job seed. A job NAME is a deployment fact, so this kernel names none. The
80
+ -- claim creates the control row it needs on first contact;
81
+ -- * no lease extension and no reaper. Neither exists upstream either. A lease that
82
+ -- elapses is simply claimable again, and a job that must not be re-entered before
83
+ -- its own work finishes must ask for a lease long enough to cover it;
84
+ -- * no attempt counter and no poison-pill policy. Upstream has none on this ledger,
85
+ -- and inventing one here would be a policy this kernel has no way to falsify;
86
+ -- * no scheduling. WHEN a job runs is the host's problem. This ledger only answers
87
+ -- whether an attempt that has already begun is allowed to proceed.
88
+
89
+ CREATE TABLE IF NOT EXISTS public.platform_job_controls (
90
+ job_name text PRIMARY KEY,
91
+ created_at timestamptz NOT NULL DEFAULT now(),
92
+ updated_at timestamptz NOT NULL DEFAULT now(),
93
+ enabled boolean NOT NULL DEFAULT true,
94
+ active_driver text NOT NULL
95
+ CHECK (active_driver IN ('worker', 'scheduler', 'operator')),
96
+ lease_owner text,
97
+ lease_token uuid,
98
+ lease_until timestamptz,
99
+ last_started_at timestamptz,
100
+ last_finished_at timestamptz,
101
+ last_success_at timestamptz,
102
+ last_status text
103
+ CHECK (last_status IS NULL OR last_status IN ('running', 'success', 'failed', 'skipped')),
104
+ last_run_id uuid,
105
+ metadata jsonb NOT NULL DEFAULT '{}'::jsonb
106
+ );
107
+
108
+ CREATE TABLE IF NOT EXISTS public.platform_job_runs (
109
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
110
+ job_name text NOT NULL REFERENCES public.platform_job_controls(job_name),
111
+ created_at timestamptz NOT NULL DEFAULT now(),
112
+ started_at timestamptz NOT NULL DEFAULT now(),
113
+ finished_at timestamptz,
114
+ status text NOT NULL DEFAULT 'running'
115
+ CHECK (status IN ('running', 'success', 'failed', 'skipped')),
116
+ driver text NOT NULL
117
+ CHECK (driver IN ('worker', 'scheduler', 'operator')),
118
+ lease_until timestamptz,
119
+ checked integer CHECK (checked IS NULL OR checked >= 0),
120
+ updated integer CHECK (updated IS NULL OR updated >= 0),
121
+ error text,
122
+ support_code text,
123
+ metadata jsonb NOT NULL DEFAULT '{}'::jsonb
124
+ );
125
+
126
+ CREATE INDEX IF NOT EXISTS idx_platform_job_runs_job_started
127
+ ON public.platform_job_runs (job_name, started_at DESC);
128
+
129
+ CREATE INDEX IF NOT EXISTS idx_platform_job_runs_status_started
130
+ ON public.platform_job_runs (status, started_at DESC);
131
+
132
+ COMMENT ON TABLE public.platform_job_controls IS
133
+ 'One row per scheduled job: whether it may run, which runner class owns it, and the live lease.';
134
+ COMMENT ON TABLE public.platform_job_runs IS
135
+ 'Append-only attempt ledger. A refused attempt is recorded as a skipped run, so a job that never runs is visible.';
136
+ COMMENT ON COLUMN public.platform_job_runs.driver IS
137
+ 'Neutral runner class of the attempt. The concrete adapter, if any, lives in metadata as evidence.';
138
+
139
+ CREATE OR REPLACE FUNCTION public.platform_claim_job_run(
140
+ p_job_name text,
141
+ p_driver text,
142
+ p_lease_seconds integer DEFAULT 900,
143
+ p_metadata jsonb DEFAULT '{}'::jsonb
144
+ )
145
+ RETURNS TABLE (
146
+ acquired boolean,
147
+ run_id uuid,
148
+ reason text,
149
+ lease_until timestamptz
150
+ )
151
+ LANGUAGE plpgsql
152
+ SET search_path = public, pg_catalog
153
+ AS $$
154
+ DECLARE
155
+ v_control public.platform_job_controls%ROWTYPE;
156
+ v_job_name text := btrim(COALESCE(p_job_name, ''));
157
+ v_driver text := COALESCE(NULLIF(btrim(p_driver), ''), 'worker');
158
+ v_lease_seconds integer;
159
+ v_run_id uuid;
160
+ v_now timestamptz := now();
161
+ v_metadata jsonb := COALESCE(p_metadata, '{}'::jsonb);
162
+ v_lease_until timestamptz;
163
+ v_refusal text;
164
+ BEGIN
165
+ IF v_job_name = '' THEN
166
+ RAISE EXCEPTION 'platform_job_name_required' USING ERRCODE = '22023';
167
+ END IF;
168
+
169
+ IF v_driver NOT IN ('worker', 'scheduler', 'operator') THEN
170
+ RAISE EXCEPTION 'platform_job_invalid_driver' USING ERRCODE = '22023';
171
+ END IF;
172
+
173
+ v_lease_seconds := LEAST(GREATEST(COALESCE(p_lease_seconds, 900), 60), 3600);
174
+
175
+ INSERT INTO public.platform_job_controls (job_name, active_driver)
176
+ VALUES (v_job_name, v_driver)
177
+ ON CONFLICT (job_name) DO NOTHING;
178
+
179
+ -- The row lock IS the exclusivity. Everything below runs with the control row held.
180
+ SELECT * INTO v_control
181
+ FROM public.platform_job_controls
182
+ WHERE public.platform_job_controls.job_name = v_job_name
183
+ FOR UPDATE;
184
+
185
+ v_lease_until := v_control.lease_until;
186
+ v_refusal := CASE
187
+ WHEN NOT v_control.enabled THEN 'job_disabled'
188
+ WHEN v_control.active_driver <> v_driver THEN 'inactive_driver'
189
+ WHEN v_lease_until IS NOT NULL AND v_lease_until > v_now THEN 'lease_active'
190
+ ELSE NULL
191
+ END;
192
+
193
+ IF v_refusal IS NOT NULL THEN
194
+ -- A refusal is still an attempt, and an attempt nobody records is an outage
195
+ -- nobody can see. The refused run carries WHY, and the control row records that
196
+ -- somebody tried; the live lease is left exactly as it was found.
197
+ INSERT INTO public.platform_job_runs (
198
+ job_name, status, driver, started_at, finished_at, lease_until, error, metadata
199
+ )
200
+ VALUES (
201
+ v_job_name, 'skipped', v_driver, v_now, v_now,
202
+ CASE WHEN v_refusal = 'lease_active' THEN v_lease_until ELSE NULL END,
203
+ v_refusal,
204
+ v_metadata || jsonb_build_object('activeDriver', v_control.active_driver, 'leaseOwner', v_control.lease_owner)
205
+ )
206
+ RETURNING id INTO v_run_id;
207
+
208
+ UPDATE public.platform_job_controls
209
+ SET updated_at = v_now,
210
+ last_started_at = v_now,
211
+ last_finished_at = v_now,
212
+ last_status = 'skipped',
213
+ last_run_id = v_run_id
214
+ WHERE public.platform_job_controls.job_name = v_job_name;
215
+
216
+ RETURN QUERY SELECT false, v_run_id, v_refusal,
217
+ CASE WHEN v_refusal = 'lease_active' THEN v_lease_until ELSE NULL::timestamptz END;
218
+ RETURN;
219
+ END IF;
220
+
221
+ v_run_id := gen_random_uuid();
222
+ v_lease_until := v_now + make_interval(secs => v_lease_seconds);
223
+
224
+ UPDATE public.platform_job_controls
225
+ SET updated_at = v_now,
226
+ lease_owner = v_driver,
227
+ lease_token = v_run_id,
228
+ lease_until = v_lease_until,
229
+ last_started_at = v_now,
230
+ last_status = 'running',
231
+ last_run_id = v_run_id
232
+ WHERE public.platform_job_controls.job_name = v_job_name;
233
+
234
+ INSERT INTO public.platform_job_runs (
235
+ id, job_name, status, driver, started_at, lease_until, metadata
236
+ )
237
+ VALUES (
238
+ v_run_id, v_job_name, 'running', v_driver, v_now, v_lease_until, v_metadata
239
+ );
240
+
241
+ RETURN QUERY SELECT true, v_run_id, 'acquired', v_lease_until;
242
+ END;
243
+ $$;
244
+
245
+ CREATE OR REPLACE FUNCTION public.platform_finish_job_run_v2(
246
+ p_job_name text,
247
+ p_run_id uuid,
248
+ p_status text,
249
+ p_checked integer DEFAULT NULL,
250
+ p_updated integer DEFAULT NULL,
251
+ p_error text DEFAULT NULL,
252
+ p_support_code text DEFAULT NULL,
253
+ p_metadata jsonb DEFAULT '{}'::jsonb
254
+ )
255
+ RETURNS boolean
256
+ LANGUAGE plpgsql
257
+ SET search_path = public, pg_catalog
258
+ AS $$
259
+ DECLARE
260
+ v_now timestamptz := now();
261
+ v_job_name text := btrim(COALESCE(p_job_name, ''));
262
+ v_metadata jsonb := COALESCE(p_metadata, '{}'::jsonb);
263
+ v_released boolean;
264
+ BEGIN
265
+ IF p_status IS NULL OR p_status NOT IN ('success', 'failed') THEN
266
+ RAISE EXCEPTION 'platform_job_invalid_finish_status' USING ERRCODE = '22023';
267
+ END IF;
268
+
269
+ -- The run row is always stamped, even by a runner whose lease is gone: a runner that
270
+ -- lost its lease still owes the ledger the truth about what it did. Deliberately NOT
271
+ -- guarded on `finished_at IS NULL` -- upstream re-stamps too, and a kernel that
272
+ -- silently ignored a second finish would answer a different last-writer than the
273
+ -- bundle it is compared against.
274
+ UPDATE public.platform_job_runs
275
+ SET status = p_status,
276
+ finished_at = v_now,
277
+ checked = p_checked,
278
+ updated = p_updated,
279
+ error = p_error,
280
+ support_code = p_support_code,
281
+ metadata = public.platform_job_runs.metadata || v_metadata
282
+ WHERE id = p_run_id
283
+ AND public.platform_job_runs.job_name = v_job_name;
284
+
285
+ -- The lease is released ONLY by the token that holds it. This predicate is the fence.
286
+ UPDATE public.platform_job_controls
287
+ SET updated_at = v_now,
288
+ lease_owner = NULL,
289
+ lease_token = NULL,
290
+ lease_until = NULL,
291
+ last_finished_at = v_now,
292
+ last_success_at = CASE WHEN p_status = 'success' THEN v_now ELSE last_success_at END,
293
+ last_status = p_status,
294
+ last_run_id = p_run_id
295
+ WHERE public.platform_job_controls.job_name = v_job_name
296
+ AND lease_token = p_run_id
297
+ RETURNING true INTO v_released;
298
+
299
+ RETURN COALESCE(v_released, false);
300
+ END;
301
+ $$;
@@ -0,0 +1,70 @@
1
+ -- Job-run ledger v3 entry points: invocation-aware claim and finish.
2
+ --
3
+ -- Applies after 20260809150000_job_run_ledger.sql, whose v2 claim and finish these delegate to.
4
+ -- The trigger kind and invocation source are evidence folded into the run metadata, never an
5
+ -- authorization control. CREATE OR REPLACE lets a database whose own migration chain already
6
+ -- defines these functions with the same bodies apply this file without change.
7
+
8
+ CREATE OR REPLACE FUNCTION public.platform_claim_job_run_v3(
9
+ p_job_name text,
10
+ p_trigger_kind text,
11
+ p_invocation_source text,
12
+ p_lease_seconds integer DEFAULT 900,
13
+ p_metadata jsonb DEFAULT '{}'::jsonb
14
+ )
15
+ RETURNS TABLE (
16
+ acquired boolean,
17
+ run_id uuid,
18
+ reason text,
19
+ lease_until timestamptz
20
+ )
21
+ LANGUAGE plpgsql
22
+ SET search_path = public, pg_catalog
23
+ AS $$
24
+ DECLARE
25
+ v_trigger_kind text := COALESCE(NULLIF(btrim(p_trigger_kind), ''), '');
26
+ v_invocation_source text := COALESCE(NULLIF(btrim(p_invocation_source), ''), '');
27
+ BEGIN
28
+ IF p_job_name IS NULL OR btrim(p_job_name) = '' THEN
29
+ RAISE EXCEPTION 'platform_job_name_required' USING ERRCODE = '22023';
30
+ END IF;
31
+
32
+ IF v_trigger_kind NOT IN ('worker', 'scheduler', 'operator') THEN
33
+ RAISE EXCEPTION 'platform_job_invalid_trigger_kind' USING ERRCODE = '22023';
34
+ END IF;
35
+
36
+ IF v_invocation_source = '' OR length(v_invocation_source) > 128 THEN
37
+ RAISE EXCEPTION 'platform_job_invocation_source_required' USING ERRCODE = '22023';
38
+ END IF;
39
+
40
+ -- The invocation source is evidence, never an authorization control, so it is folded into
41
+ -- the metadata this ledger stores and never interprets.
42
+ RETURN QUERY
43
+ SELECT claimed.acquired, claimed.run_id, claimed.reason, claimed.lease_until
44
+ FROM public.platform_claim_job_run(
45
+ p_job_name,
46
+ v_trigger_kind,
47
+ p_lease_seconds,
48
+ COALESCE(p_metadata, '{}'::jsonb) || jsonb_build_object(
49
+ 'triggerKind', v_trigger_kind, 'invocationSource', v_invocation_source)
50
+ ) AS claimed;
51
+ END;
52
+ $$;
53
+
54
+ CREATE OR REPLACE FUNCTION public.platform_finish_job_run_v3(
55
+ p_job_name text,
56
+ p_run_id uuid,
57
+ p_status text,
58
+ p_checked integer DEFAULT NULL,
59
+ p_updated integer DEFAULT NULL,
60
+ p_error text DEFAULT NULL,
61
+ p_support_code text DEFAULT NULL,
62
+ p_metadata jsonb DEFAULT '{}'::jsonb
63
+ )
64
+ RETURNS boolean
65
+ LANGUAGE sql
66
+ SET search_path = public, pg_catalog
67
+ AS $$
68
+ SELECT public.platform_finish_job_run_v2(
69
+ p_job_name, p_run_id, p_status, p_checked, p_updated, p_error, p_support_code, p_metadata);
70
+ $$;
@@ -0,0 +1,40 @@
1
+ -- Public self-host actor-auth bootstrap.
2
+ --
3
+ -- This append-only platform forward establishes only the default-deny actor
4
+ -- roles and `auth.uid()` primitive required by a future actor-scoped
5
+ -- capability. The node-postgres runtime applies this exact manifest unit
6
+ -- before listening; it does not select the historical private compatibility
7
+ -- bootstrap. No customer mapping, table privilege, policy, verifier, managed
8
+ -- migration, service role, or additional auth helper is introduced here.
9
+ --
10
+ -- openlup:allow-grant: grants only auth schema usage and auth.uid() execution to the two
11
+ -- default-deny actor roles; fresh final-image Compose proof verifies their exact ACLs and RLS lane.
12
+
13
+ CREATE ROLE anon NOLOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
14
+ CREATE ROLE authenticated NOLOGIN NOINHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;
15
+
16
+ CREATE SCHEMA auth;
17
+
18
+ CREATE FUNCTION auth.uid()
19
+ RETURNS uuid
20
+ LANGUAGE sql
21
+ STABLE
22
+ SET search_path = pg_catalog
23
+ AS $$
24
+ SELECT CASE
25
+ WHEN current_setting('request.jwt.claims', true) IS NULL THEN NULL
26
+ WHEN current_setting('request.jwt.claims', true) = '' THEN NULL
27
+ WHEN jsonb_typeof(current_setting('request.jwt.claims', true)::jsonb -> 'sub') <> 'string' THEN NULL
28
+ WHEN current_setting('request.jwt.claims', true)::jsonb ->> 'sub'
29
+ !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' THEN NULL
30
+ ELSE (current_setting('request.jwt.claims', true)::jsonb ->> 'sub')::uuid
31
+ END
32
+ $$;
33
+
34
+ REVOKE ALL ON SCHEMA auth FROM PUBLIC;
35
+ REVOKE ALL ON FUNCTION auth.uid() FROM PUBLIC;
36
+ GRANT USAGE ON SCHEMA auth TO anon, authenticated;
37
+ GRANT EXECUTE ON FUNCTION auth.uid() TO anon, authenticated;
38
+
39
+ GRANT anon TO CURRENT_USER WITH INHERIT FALSE, SET TRUE, ADMIN FALSE;
40
+ GRANT authenticated TO CURRENT_USER WITH INHERIT FALSE, SET TRUE, ADMIN FALSE;
@@ -0,0 +1,31 @@
1
+ -- Customer identity read catalog forward.
2
+ --
3
+ -- This append-only delta adds only an optional neutral principal link to the
4
+ -- existing client profile. Authenticated callers can select their own bounded
5
+ -- response columns through the existing invoker-rights actor lane. It does not
6
+ -- add a customer mutation, privileged role, auth table, routine, or trigger.
7
+ --
8
+ -- openlup:allow-unique-index: a non-null principal maps to at most one client; the fresh-database proof verifies the exact partial index.
9
+ -- openlup:allow-rls: the one authenticated SELECT policy is proven through the shipped actor transaction with two principals.
10
+ -- openlup:allow-grant: PUBLIC and anon remain denied; authenticated receives only the five profile response columns, proven by catalog ACL checks.
11
+
12
+ ALTER TABLE public.clients ADD COLUMN principal_id uuid;
13
+
14
+ -- squawk-ignore require-concurrent-index-creation
15
+ CREATE UNIQUE INDEX clients_principal_id_unique
16
+ ON public.clients (principal_id)
17
+ WHERE principal_id IS NOT NULL;
18
+
19
+ ALTER TABLE public.clients ENABLE ROW LEVEL SECURITY;
20
+
21
+ CREATE POLICY clients_select_own_principal
22
+ ON public.clients
23
+ FOR SELECT
24
+ TO authenticated
25
+ USING (principal_id = auth.uid());
26
+
27
+ REVOKE ALL ON TABLE public.clients FROM PUBLIC;
28
+ REVOKE ALL ON TABLE public.clients FROM anon;
29
+ REVOKE ALL ON TABLE public.clients FROM authenticated;
30
+ GRANT SELECT (id, email, first_name, last_name, lifecycle_stage)
31
+ ON TABLE public.clients TO authenticated;