@baruk/core 0.3.0 → 0.4.2

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 (708) hide show
  1. package/.baruk-provenance.json +10 -0
  2. package/CHANGELOG.md +97 -0
  3. package/README.md +9 -2
  4. package/dist/auth/password-reset.d.ts +30 -1
  5. package/dist/auth/password-reset.d.ts.map +1 -1
  6. package/dist/auth/password-reset.js +139 -11
  7. package/dist/auth/password-reset.js.map +1 -1
  8. package/dist/auth/service.d.ts +39 -2
  9. package/dist/auth/service.d.ts.map +1 -1
  10. package/dist/auth/service.js +75 -10
  11. package/dist/auth/service.js.map +1 -1
  12. package/dist/billing/ar-repository.d.ts +334 -0
  13. package/dist/billing/ar-repository.d.ts.map +1 -0
  14. package/dist/billing/ar-repository.js +707 -0
  15. package/dist/billing/ar-repository.js.map +1 -0
  16. package/dist/billing/auto-generation.d.ts +87 -0
  17. package/dist/billing/auto-generation.d.ts.map +1 -0
  18. package/dist/billing/auto-generation.js +181 -0
  19. package/dist/billing/auto-generation.js.map +1 -0
  20. package/dist/billing/live-guard.d.ts +64 -0
  21. package/dist/billing/live-guard.d.ts.map +1 -0
  22. package/dist/billing/live-guard.js +68 -0
  23. package/dist/billing/live-guard.js.map +1 -0
  24. package/dist/billing/payment-means-catalog.d.ts +17 -0
  25. package/dist/billing/payment-means-catalog.d.ts.map +1 -0
  26. package/dist/billing/payment-means-catalog.js +21 -0
  27. package/dist/billing/payment-means-catalog.js.map +1 -0
  28. package/dist/billing/payment-means.d.ts +38 -0
  29. package/dist/billing/payment-means.d.ts.map +1 -0
  30. package/dist/billing/payment-means.js +29 -0
  31. package/dist/billing/payment-means.js.map +1 -0
  32. package/dist/billing/payment-rail.d.ts +118 -0
  33. package/dist/billing/payment-rail.d.ts.map +1 -0
  34. package/dist/billing/payment-rail.js +295 -0
  35. package/dist/billing/payment-rail.js.map +1 -0
  36. package/dist/billing/provider/asaas.d.ts +46 -1
  37. package/dist/billing/provider/asaas.d.ts.map +1 -1
  38. package/dist/billing/provider/asaas.js +194 -14
  39. package/dist/billing/provider/asaas.js.map +1 -1
  40. package/dist/billing/provider/manual.js +1 -1
  41. package/dist/billing/provider/manual.js.map +1 -1
  42. package/dist/billing/provider/types.d.ts +115 -8
  43. package/dist/billing/provider/types.d.ts.map +1 -1
  44. package/dist/billing/provider-account.d.ts +89 -0
  45. package/dist/billing/provider-account.d.ts.map +1 -0
  46. package/dist/billing/provider-account.js +161 -0
  47. package/dist/billing/provider-account.js.map +1 -0
  48. package/dist/billing/provider-onboarding.d.ts +67 -0
  49. package/dist/billing/provider-onboarding.d.ts.map +1 -0
  50. package/dist/billing/provider-onboarding.js +234 -0
  51. package/dist/billing/provider-onboarding.js.map +1 -0
  52. package/dist/billing/receivable.d.ts +5 -1
  53. package/dist/billing/receivable.d.ts.map +1 -1
  54. package/dist/billing/reconcile.d.ts +58 -0
  55. package/dist/billing/reconcile.d.ts.map +1 -0
  56. package/dist/billing/reconcile.js +138 -0
  57. package/dist/billing/reconcile.js.map +1 -0
  58. package/dist/billing/repository.d.ts +90 -1
  59. package/dist/billing/repository.d.ts.map +1 -1
  60. package/dist/billing/repository.js +148 -19
  61. package/dist/billing/repository.js.map +1 -1
  62. package/dist/cjs/auth/password-reset.js +141 -11
  63. package/dist/cjs/auth/password-reset.js.map +1 -1
  64. package/dist/cjs/auth/service.js +75 -9
  65. package/dist/cjs/auth/service.js.map +1 -1
  66. package/dist/cjs/billing/ar-repository.js +728 -0
  67. package/dist/cjs/billing/ar-repository.js.map +1 -0
  68. package/dist/cjs/billing/auto-generation.js +186 -0
  69. package/dist/cjs/billing/auto-generation.js.map +1 -0
  70. package/dist/cjs/billing/live-guard.js +72 -0
  71. package/dist/cjs/billing/live-guard.js.map +1 -0
  72. package/dist/cjs/billing/payment-means-catalog.js +25 -0
  73. package/dist/cjs/billing/payment-means-catalog.js.map +1 -0
  74. package/dist/cjs/billing/payment-means.js +33 -0
  75. package/dist/cjs/billing/payment-means.js.map +1 -0
  76. package/dist/cjs/billing/payment-rail.js +301 -0
  77. package/dist/cjs/billing/payment-rail.js.map +1 -0
  78. package/dist/cjs/billing/provider/asaas.js +194 -14
  79. package/dist/cjs/billing/provider/asaas.js.map +1 -1
  80. package/dist/cjs/billing/provider/manual.js +1 -1
  81. package/dist/cjs/billing/provider/manual.js.map +1 -1
  82. package/dist/cjs/billing/provider-account.js +170 -0
  83. package/dist/cjs/billing/provider-account.js.map +1 -0
  84. package/dist/cjs/billing/provider-onboarding.js +239 -0
  85. package/dist/cjs/billing/provider-onboarding.js.map +1 -0
  86. package/dist/cjs/billing/reconcile.js +143 -0
  87. package/dist/cjs/billing/reconcile.js.map +1 -0
  88. package/dist/cjs/billing/repository.js +150 -18
  89. package/dist/cjs/billing/repository.js.map +1 -1
  90. package/dist/cjs/clients/repository.js +96 -20
  91. package/dist/cjs/clients/repository.js.map +1 -1
  92. package/dist/cjs/collection/branding.js +140 -0
  93. package/dist/cjs/collection/branding.js.map +1 -0
  94. package/dist/cjs/collection/channel-accounts.js +137 -0
  95. package/dist/cjs/collection/channel-accounts.js.map +1 -0
  96. package/dist/cjs/collection/channel.js +17 -0
  97. package/dist/cjs/collection/channel.js.map +1 -0
  98. package/dist/cjs/collection/communication-bridge.js +140 -0
  99. package/dist/cjs/collection/communication-bridge.js.map +1 -0
  100. package/dist/cjs/collection/communication-runtime.js +193 -0
  101. package/dist/cjs/collection/communication-runtime.js.map +1 -0
  102. package/dist/cjs/collection/consent-repository.js +84 -0
  103. package/dist/cjs/collection/consent-repository.js.map +1 -0
  104. package/dist/cjs/collection/copy.js +125 -0
  105. package/dist/cjs/collection/copy.js.map +1 -0
  106. package/dist/cjs/collection/delivery-callback.js +151 -0
  107. package/dist/cjs/collection/delivery-callback.js.map +1 -0
  108. package/dist/cjs/collection/email-layout.js +252 -0
  109. package/dist/cjs/collection/email-layout.js.map +1 -0
  110. package/dist/cjs/collection/engine.js +414 -0
  111. package/dist/cjs/collection/engine.js.map +1 -0
  112. package/dist/cjs/collection/format.js +76 -0
  113. package/dist/cjs/collection/format.js.map +1 -0
  114. package/dist/cjs/collection/payment-block.js +3 -0
  115. package/dist/cjs/collection/payment-block.js.map +1 -0
  116. package/dist/cjs/collection/policy.js +147 -0
  117. package/dist/cjs/collection/policy.js.map +1 -0
  118. package/dist/cjs/collection/repository.js +260 -0
  119. package/dist/cjs/collection/repository.js.map +1 -0
  120. package/dist/cjs/collection/subject.js +104 -0
  121. package/dist/cjs/collection/subject.js.map +1 -0
  122. package/dist/cjs/collection/template-catalog.js +165 -0
  123. package/dist/cjs/collection/template-catalog.js.map +1 -0
  124. package/dist/cjs/collection/template.js +78 -0
  125. package/dist/cjs/collection/template.js.map +1 -0
  126. package/dist/cjs/communication/adapters/email.js +132 -0
  127. package/dist/cjs/communication/adapters/email.js.map +1 -0
  128. package/dist/cjs/communication/adapters/sms.js +127 -0
  129. package/dist/cjs/communication/adapters/sms.js.map +1 -0
  130. package/dist/cjs/communication/adapters/whatsapp-language.js +39 -0
  131. package/dist/cjs/communication/adapters/whatsapp-language.js.map +1 -0
  132. package/dist/cjs/communication/adapters/whatsapp-templates.js +100 -0
  133. package/dist/cjs/communication/adapters/whatsapp-templates.js.map +1 -0
  134. package/dist/cjs/communication/adapters/whatsapp.js +213 -0
  135. package/dist/cjs/communication/adapters/whatsapp.js.map +1 -0
  136. package/dist/cjs/communication/audit.js +61 -0
  137. package/dist/cjs/communication/audit.js.map +1 -0
  138. package/dist/cjs/communication/capabilities.js +76 -0
  139. package/dist/cjs/communication/capabilities.js.map +1 -0
  140. package/dist/cjs/communication/channel.js +53 -0
  141. package/dist/cjs/communication/channel.js.map +1 -0
  142. package/dist/cjs/communication/claim.js +228 -0
  143. package/dist/cjs/communication/claim.js.map +1 -0
  144. package/dist/cjs/communication/consent.js +43 -0
  145. package/dist/cjs/communication/consent.js.map +1 -0
  146. package/dist/cjs/communication/context.js +45 -0
  147. package/dist/cjs/communication/context.js.map +1 -0
  148. package/dist/cjs/communication/credentials.js +121 -0
  149. package/dist/cjs/communication/credentials.js.map +1 -0
  150. package/dist/cjs/communication/dry-run-scenario.js +161 -0
  151. package/dist/cjs/communication/dry-run-scenario.js.map +1 -0
  152. package/dist/cjs/communication/dry-run.js +97 -0
  153. package/dist/cjs/communication/dry-run.js.map +1 -0
  154. package/dist/cjs/communication/engine.js +483 -0
  155. package/dist/cjs/communication/engine.js.map +1 -0
  156. package/dist/cjs/communication/graph.js +50 -0
  157. package/dist/cjs/communication/graph.js.map +1 -0
  158. package/dist/cjs/communication/idempotency.js +26 -0
  159. package/dist/cjs/communication/idempotency.js.map +1 -0
  160. package/dist/cjs/communication/identity.js +110 -0
  161. package/dist/cjs/communication/identity.js.map +1 -0
  162. package/dist/cjs/communication/index.js +135 -0
  163. package/dist/cjs/communication/index.js.map +1 -0
  164. package/dist/cjs/communication/onboarding.js +297 -0
  165. package/dist/cjs/communication/onboarding.js.map +1 -0
  166. package/dist/cjs/communication/policy.js +16 -0
  167. package/dist/cjs/communication/policy.js.map +1 -0
  168. package/dist/cjs/communication/routing.js +77 -0
  169. package/dist/cjs/communication/routing.js.map +1 -0
  170. package/dist/cjs/communication/sender-profile.js +128 -0
  171. package/dist/cjs/communication/sender-profile.js.map +1 -0
  172. package/dist/cjs/communication/status.js +86 -0
  173. package/dist/cjs/communication/status.js.map +1 -0
  174. package/dist/cjs/communication/stores/onboarding.js +135 -0
  175. package/dist/cjs/communication/stores/onboarding.js.map +1 -0
  176. package/dist/cjs/communication/stores/postgres.js +458 -0
  177. package/dist/cjs/communication/stores/postgres.js.map +1 -0
  178. package/dist/cjs/communication/stores/routing.js +334 -0
  179. package/dist/cjs/communication/stores/routing.js.map +1 -0
  180. package/dist/cjs/communication/stores/secrets.js +126 -0
  181. package/dist/cjs/communication/stores/secrets.js.map +1 -0
  182. package/dist/cjs/communication/template-provider.js +22 -0
  183. package/dist/cjs/communication/template-provider.js.map +1 -0
  184. package/dist/cjs/communication/template.js +63 -0
  185. package/dist/cjs/communication/template.js.map +1 -0
  186. package/dist/cjs/communication/webhook-subscription.js +258 -0
  187. package/dist/cjs/communication/webhook-subscription.js.map +1 -0
  188. package/dist/cjs/communication/webhook.js +292 -0
  189. package/dist/cjs/communication/webhook.js.map +1 -0
  190. package/dist/cjs/competence/repository.js +309 -0
  191. package/dist/cjs/competence/repository.js.map +1 -0
  192. package/dist/cjs/contacts/phone.js +192 -0
  193. package/dist/cjs/contacts/phone.js.map +1 -0
  194. package/dist/cjs/db/seed-catalog.js +48 -0
  195. package/dist/cjs/db/seed-catalog.js.map +1 -0
  196. package/dist/cjs/db/seed.js +15 -7
  197. package/dist/cjs/db/seed.js.map +1 -1
  198. package/dist/cjs/documents/repository.js +244 -0
  199. package/dist/cjs/documents/repository.js.map +1 -0
  200. package/dist/cjs/documents/requests.js +136 -0
  201. package/dist/cjs/documents/requests.js.map +1 -0
  202. package/dist/cjs/index.js +427 -48
  203. package/dist/cjs/index.js.map +1 -1
  204. package/dist/cjs/legal/company.js +36 -0
  205. package/dist/cjs/legal/company.js.map +1 -0
  206. package/dist/cjs/legal/documents.js +671 -0
  207. package/dist/cjs/legal/documents.js.map +1 -0
  208. package/dist/cjs/legal/index.js +15 -0
  209. package/dist/cjs/legal/index.js.map +1 -0
  210. package/dist/cjs/legal/types.js +34 -0
  211. package/dist/cjs/legal/types.js.map +1 -0
  212. package/dist/cjs/mail/auth-templates.js +29 -0
  213. package/dist/cjs/mail/auth-templates.js.map +1 -0
  214. package/dist/cjs/mail/dedup-store.js +12 -51
  215. package/dist/cjs/mail/dedup-store.js.map +1 -1
  216. package/dist/cjs/mail/index.js +16 -4
  217. package/dist/cjs/mail/index.js.map +1 -1
  218. package/dist/cjs/mail/provider.js +12 -247
  219. package/dist/cjs/mail/provider.js.map +1 -1
  220. package/dist/cjs/mail/resend.js +14 -18
  221. package/dist/cjs/mail/resend.js.map +1 -1
  222. package/dist/cjs/mail/sender-resolver.js +12 -11
  223. package/dist/cjs/mail/sender-resolver.js.map +1 -1
  224. package/dist/cjs/mail/template-renderer.js +11 -16
  225. package/dist/cjs/mail/template-renderer.js.map +1 -1
  226. package/dist/cjs/mail/transactional-outbound.js +117 -0
  227. package/dist/cjs/mail/transactional-outbound.js.map +1 -0
  228. package/dist/cjs/mail/types.js +8 -35
  229. package/dist/cjs/mail/types.js.map +1 -1
  230. package/dist/cjs/portal/provisioning.js +144 -0
  231. package/dist/cjs/portal/provisioning.js.map +1 -0
  232. package/dist/cjs/pricing-engine/canonical.js +42 -0
  233. package/dist/cjs/pricing-engine/canonical.js.map +1 -0
  234. package/dist/cjs/pricing-engine/errors.js +6 -1
  235. package/dist/cjs/pricing-engine/errors.js.map +1 -1
  236. package/dist/cjs/pricing-engine/index.js +12 -1
  237. package/dist/cjs/pricing-engine/index.js.map +1 -1
  238. package/dist/cjs/pricing-engine/pipeline.js +200 -26
  239. package/dist/cjs/pricing-engine/pipeline.js.map +1 -1
  240. package/dist/cjs/pricing-engine/shadow-adapter.js +79 -0
  241. package/dist/cjs/pricing-engine/shadow-adapter.js.map +1 -0
  242. package/dist/cjs/pricing-engine/shadow-diff.js +55 -0
  243. package/dist/cjs/pricing-engine/shadow-diff.js.map +1 -0
  244. package/dist/cjs/pricing-engine/shadow-facade.js +85 -0
  245. package/dist/cjs/pricing-engine/shadow-facade.js.map +1 -0
  246. package/dist/cjs/pricing-engine/shadow-legacy-types.js +19 -0
  247. package/dist/cjs/pricing-engine/shadow-legacy-types.js.map +1 -0
  248. package/dist/cjs/pricing-engine/shadow-metrics.js +34 -0
  249. package/dist/cjs/pricing-engine/shadow-metrics.js.map +1 -0
  250. package/dist/cjs/pricing-engine/shadow-normalizer.js +42 -0
  251. package/dist/cjs/pricing-engine/shadow-normalizer.js.map +1 -0
  252. package/dist/cjs/rbac/authorize.js +28 -0
  253. package/dist/cjs/rbac/authorize.js.map +1 -1
  254. package/dist/cjs/rbac/catalog.js +67 -1
  255. package/dist/cjs/rbac/catalog.js.map +1 -1
  256. package/dist/cjs/rbac/workload-provisioning.js +160 -0
  257. package/dist/cjs/rbac/workload-provisioning.js.map +1 -0
  258. package/dist/cjs/rbac/workload.js +191 -0
  259. package/dist/cjs/rbac/workload.js.map +1 -0
  260. package/dist/cjs/storage/checksum.js +15 -0
  261. package/dist/cjs/storage/checksum.js.map +1 -0
  262. package/dist/cjs/storage/key.js +72 -0
  263. package/dist/cjs/storage/key.js.map +1 -0
  264. package/dist/cjs/storage/local.js +100 -0
  265. package/dist/cjs/storage/local.js.map +1 -0
  266. package/dist/cjs/storage/r2.js +181 -0
  267. package/dist/cjs/storage/r2.js.map +1 -0
  268. package/dist/cjs/storage/registry.js +41 -0
  269. package/dist/cjs/storage/registry.js.map +1 -0
  270. package/dist/cjs/storage/types.js +50 -0
  271. package/dist/cjs/storage/types.js.map +1 -0
  272. package/dist/cjs/subscription-billing/provider/asaas.js +32 -7
  273. package/dist/cjs/subscription-billing/provider/asaas.js.map +1 -1
  274. package/dist/cjs/subscription-billing/provider/disabled.js +8 -0
  275. package/dist/cjs/subscription-billing/provider/disabled.js.map +1 -1
  276. package/dist/cjs/subscription-billing/provider/registry.js +17 -0
  277. package/dist/cjs/subscription-billing/provider/registry.js.map +1 -1
  278. package/dist/cjs/subscription-billing/service.js +57 -8
  279. package/dist/cjs/subscription-billing/service.js.map +1 -1
  280. package/dist/cjs/subscription-billing/types.js.map +1 -1
  281. package/dist/cjs/tenancy/provisioning.js +32 -8
  282. package/dist/cjs/tenancy/provisioning.js.map +1 -1
  283. package/dist/cjs/users/repository.js +119 -2
  284. package/dist/cjs/users/repository.js.map +1 -1
  285. package/dist/cjs/vault/repository.js +81 -0
  286. package/dist/cjs/vault/repository.js.map +1 -1
  287. package/dist/clients/repository.d.ts +42 -0
  288. package/dist/clients/repository.d.ts.map +1 -1
  289. package/dist/clients/repository.js +94 -21
  290. package/dist/clients/repository.js.map +1 -1
  291. package/dist/collection/branding.d.ts +72 -0
  292. package/dist/collection/branding.d.ts.map +1 -0
  293. package/dist/collection/branding.js +136 -0
  294. package/dist/collection/branding.js.map +1 -0
  295. package/dist/collection/channel-accounts.d.ts +57 -0
  296. package/dist/collection/channel-accounts.d.ts.map +1 -0
  297. package/dist/collection/channel-accounts.js +133 -0
  298. package/dist/collection/channel-accounts.js.map +1 -0
  299. package/dist/collection/channel.d.ts +24 -0
  300. package/dist/collection/channel.d.ts.map +1 -0
  301. package/dist/collection/channel.js +16 -0
  302. package/dist/collection/channel.js.map +1 -0
  303. package/dist/collection/communication-bridge.d.ts +66 -0
  304. package/dist/collection/communication-bridge.d.ts.map +1 -0
  305. package/dist/collection/communication-bridge.js +134 -0
  306. package/dist/collection/communication-bridge.js.map +1 -0
  307. package/dist/collection/communication-runtime.d.ts +95 -0
  308. package/dist/collection/communication-runtime.d.ts.map +1 -0
  309. package/dist/collection/communication-runtime.js +187 -0
  310. package/dist/collection/communication-runtime.js.map +1 -0
  311. package/dist/collection/consent-repository.d.ts +32 -0
  312. package/dist/collection/consent-repository.d.ts.map +1 -0
  313. package/dist/collection/consent-repository.js +79 -0
  314. package/dist/collection/consent-repository.js.map +1 -0
  315. package/dist/collection/copy.d.ts +50 -0
  316. package/dist/collection/copy.d.ts.map +1 -0
  317. package/dist/collection/copy.js +122 -0
  318. package/dist/collection/copy.js.map +1 -0
  319. package/dist/collection/delivery-callback.d.ts +49 -0
  320. package/dist/collection/delivery-callback.d.ts.map +1 -0
  321. package/dist/collection/delivery-callback.js +147 -0
  322. package/dist/collection/delivery-callback.js.map +1 -0
  323. package/dist/collection/email-layout.d.ts +45 -0
  324. package/dist/collection/email-layout.d.ts.map +1 -0
  325. package/dist/collection/email-layout.js +247 -0
  326. package/dist/collection/email-layout.js.map +1 -0
  327. package/dist/collection/engine.d.ts +177 -0
  328. package/dist/collection/engine.d.ts.map +1 -0
  329. package/dist/collection/engine.js +407 -0
  330. package/dist/collection/engine.js.map +1 -0
  331. package/dist/collection/format.d.ts +32 -0
  332. package/dist/collection/format.d.ts.map +1 -0
  333. package/dist/collection/format.js +70 -0
  334. package/dist/collection/format.js.map +1 -0
  335. package/dist/collection/payment-block.d.ts +24 -0
  336. package/dist/collection/payment-block.d.ts.map +1 -0
  337. package/dist/collection/payment-block.js +2 -0
  338. package/dist/collection/payment-block.js.map +1 -0
  339. package/dist/collection/policy.d.ts +53 -0
  340. package/dist/collection/policy.d.ts.map +1 -0
  341. package/dist/collection/policy.js +141 -0
  342. package/dist/collection/policy.js.map +1 -0
  343. package/dist/collection/repository.d.ts +129 -0
  344. package/dist/collection/repository.d.ts.map +1 -0
  345. package/dist/collection/repository.js +246 -0
  346. package/dist/collection/repository.js.map +1 -0
  347. package/dist/collection/subject.d.ts +66 -0
  348. package/dist/collection/subject.d.ts.map +1 -0
  349. package/dist/collection/subject.js +99 -0
  350. package/dist/collection/subject.js.map +1 -0
  351. package/dist/collection/template-catalog.d.ts +16 -0
  352. package/dist/collection/template-catalog.d.ts.map +1 -0
  353. package/dist/collection/template-catalog.js +162 -0
  354. package/dist/collection/template-catalog.js.map +1 -0
  355. package/dist/collection/template.d.ts +37 -0
  356. package/dist/collection/template.d.ts.map +1 -0
  357. package/dist/collection/template.js +73 -0
  358. package/dist/collection/template.js.map +1 -0
  359. package/dist/communication/adapters/email.d.ts +28 -0
  360. package/dist/communication/adapters/email.d.ts.map +1 -0
  361. package/dist/communication/adapters/email.js +129 -0
  362. package/dist/communication/adapters/email.js.map +1 -0
  363. package/dist/communication/adapters/sms.d.ts +95 -0
  364. package/dist/communication/adapters/sms.d.ts.map +1 -0
  365. package/dist/communication/adapters/sms.js +121 -0
  366. package/dist/communication/adapters/sms.js.map +1 -0
  367. package/dist/communication/adapters/whatsapp-language.d.ts +34 -0
  368. package/dist/communication/adapters/whatsapp-language.d.ts.map +1 -0
  369. package/dist/communication/adapters/whatsapp-language.js +36 -0
  370. package/dist/communication/adapters/whatsapp-language.js.map +1 -0
  371. package/dist/communication/adapters/whatsapp-templates.d.ts +18 -0
  372. package/dist/communication/adapters/whatsapp-templates.d.ts.map +1 -0
  373. package/dist/communication/adapters/whatsapp-templates.js +97 -0
  374. package/dist/communication/adapters/whatsapp-templates.js.map +1 -0
  375. package/dist/communication/adapters/whatsapp.d.ts +29 -0
  376. package/dist/communication/adapters/whatsapp.d.ts.map +1 -0
  377. package/dist/communication/adapters/whatsapp.js +210 -0
  378. package/dist/communication/adapters/whatsapp.js.map +1 -0
  379. package/dist/communication/audit.d.ts +105 -0
  380. package/dist/communication/audit.d.ts.map +1 -0
  381. package/dist/communication/audit.js +56 -0
  382. package/dist/communication/audit.js.map +1 -0
  383. package/dist/communication/capabilities.d.ts +88 -0
  384. package/dist/communication/capabilities.d.ts.map +1 -0
  385. package/dist/communication/capabilities.js +72 -0
  386. package/dist/communication/capabilities.js.map +1 -0
  387. package/dist/communication/channel.d.ts +125 -0
  388. package/dist/communication/channel.d.ts.map +1 -0
  389. package/dist/communication/channel.js +47 -0
  390. package/dist/communication/channel.js.map +1 -0
  391. package/dist/communication/claim.d.ts +368 -0
  392. package/dist/communication/claim.d.ts.map +1 -0
  393. package/dist/communication/claim.js +224 -0
  394. package/dist/communication/claim.js.map +1 -0
  395. package/dist/communication/consent.d.ts +65 -0
  396. package/dist/communication/consent.d.ts.map +1 -0
  397. package/dist/communication/consent.js +40 -0
  398. package/dist/communication/consent.js.map +1 -0
  399. package/dist/communication/context.d.ts +48 -0
  400. package/dist/communication/context.d.ts.map +1 -0
  401. package/dist/communication/context.js +41 -0
  402. package/dist/communication/context.js.map +1 -0
  403. package/dist/communication/credentials.d.ts +74 -0
  404. package/dist/communication/credentials.d.ts.map +1 -0
  405. package/dist/communication/credentials.js +115 -0
  406. package/dist/communication/credentials.js.map +1 -0
  407. package/dist/communication/dry-run-scenario.d.ts +37 -0
  408. package/dist/communication/dry-run-scenario.d.ts.map +1 -0
  409. package/dist/communication/dry-run-scenario.js +158 -0
  410. package/dist/communication/dry-run-scenario.js.map +1 -0
  411. package/dist/communication/dry-run.d.ts +40 -0
  412. package/dist/communication/dry-run.d.ts.map +1 -0
  413. package/dist/communication/dry-run.js +92 -0
  414. package/dist/communication/dry-run.js.map +1 -0
  415. package/dist/communication/engine.d.ts +153 -0
  416. package/dist/communication/engine.d.ts.map +1 -0
  417. package/dist/communication/engine.js +480 -0
  418. package/dist/communication/engine.js.map +1 -0
  419. package/dist/communication/graph.d.ts +38 -0
  420. package/dist/communication/graph.d.ts.map +1 -0
  421. package/dist/communication/graph.js +45 -0
  422. package/dist/communication/graph.js.map +1 -0
  423. package/dist/communication/idempotency.d.ts +33 -0
  424. package/dist/communication/idempotency.d.ts.map +1 -0
  425. package/dist/communication/idempotency.js +23 -0
  426. package/dist/communication/idempotency.js.map +1 -0
  427. package/dist/communication/identity.d.ts +127 -0
  428. package/dist/communication/identity.d.ts.map +1 -0
  429. package/dist/communication/identity.js +105 -0
  430. package/dist/communication/identity.js.map +1 -0
  431. package/dist/communication/index.d.ts +66 -0
  432. package/dist/communication/index.d.ts.map +1 -0
  433. package/dist/communication/index.js +42 -0
  434. package/dist/communication/index.js.map +1 -0
  435. package/dist/communication/onboarding.d.ts +223 -0
  436. package/dist/communication/onboarding.d.ts.map +1 -0
  437. package/dist/communication/onboarding.js +287 -0
  438. package/dist/communication/onboarding.js.map +1 -0
  439. package/dist/communication/policy.d.ts +99 -0
  440. package/dist/communication/policy.d.ts.map +1 -0
  441. package/dist/communication/policy.js +13 -0
  442. package/dist/communication/policy.js.map +1 -0
  443. package/dist/communication/routing.d.ts +99 -0
  444. package/dist/communication/routing.d.ts.map +1 -0
  445. package/dist/communication/routing.js +73 -0
  446. package/dist/communication/routing.js.map +1 -0
  447. package/dist/communication/sender-profile.d.ts +100 -0
  448. package/dist/communication/sender-profile.d.ts.map +1 -0
  449. package/dist/communication/sender-profile.js +123 -0
  450. package/dist/communication/sender-profile.js.map +1 -0
  451. package/dist/communication/status.d.ts +48 -0
  452. package/dist/communication/status.d.ts.map +1 -0
  453. package/dist/communication/status.js +79 -0
  454. package/dist/communication/status.js.map +1 -0
  455. package/dist/communication/stores/onboarding.d.ts +88 -0
  456. package/dist/communication/stores/onboarding.d.ts.map +1 -0
  457. package/dist/communication/stores/onboarding.js +127 -0
  458. package/dist/communication/stores/onboarding.js.map +1 -0
  459. package/dist/communication/stores/postgres.d.ts +81 -0
  460. package/dist/communication/stores/postgres.d.ts.map +1 -0
  461. package/dist/communication/stores/postgres.js +453 -0
  462. package/dist/communication/stores/postgres.js.map +1 -0
  463. package/dist/communication/stores/routing.d.ts +169 -0
  464. package/dist/communication/stores/routing.d.ts.map +1 -0
  465. package/dist/communication/stores/routing.js +324 -0
  466. package/dist/communication/stores/routing.js.map +1 -0
  467. package/dist/communication/stores/secrets.d.ts +89 -0
  468. package/dist/communication/stores/secrets.d.ts.map +1 -0
  469. package/dist/communication/stores/secrets.js +122 -0
  470. package/dist/communication/stores/secrets.js.map +1 -0
  471. package/dist/communication/template-provider.d.ts +85 -0
  472. package/dist/communication/template-provider.d.ts.map +1 -0
  473. package/dist/communication/template-provider.js +21 -0
  474. package/dist/communication/template-provider.js.map +1 -0
  475. package/dist/communication/template.d.ts +87 -0
  476. package/dist/communication/template.d.ts.map +1 -0
  477. package/dist/communication/template.js +59 -0
  478. package/dist/communication/template.js.map +1 -0
  479. package/dist/communication/webhook-subscription.d.ts +156 -0
  480. package/dist/communication/webhook-subscription.d.ts.map +1 -0
  481. package/dist/communication/webhook-subscription.js +252 -0
  482. package/dist/communication/webhook-subscription.js.map +1 -0
  483. package/dist/communication/webhook.d.ts +121 -0
  484. package/dist/communication/webhook.d.ts.map +1 -0
  485. package/dist/communication/webhook.js +283 -0
  486. package/dist/communication/webhook.js.map +1 -0
  487. package/dist/competence/repository.d.ts +147 -0
  488. package/dist/competence/repository.d.ts.map +1 -0
  489. package/dist/competence/repository.js +294 -0
  490. package/dist/competence/repository.js.map +1 -0
  491. package/dist/contacts/phone.d.ts +70 -0
  492. package/dist/contacts/phone.d.ts.map +1 -0
  493. package/dist/contacts/phone.js +186 -0
  494. package/dist/contacts/phone.js.map +1 -0
  495. package/dist/db/migrate.js +17 -11
  496. package/dist/db/migrate.js.map +1 -1
  497. package/dist/db/seed-catalog.d.ts +18 -0
  498. package/dist/db/seed-catalog.d.ts.map +1 -0
  499. package/dist/db/seed-catalog.js +45 -0
  500. package/dist/db/seed-catalog.js.map +1 -0
  501. package/dist/db/seed.js +16 -8
  502. package/dist/db/seed.js.map +1 -1
  503. package/dist/documents/repository.d.ts +97 -0
  504. package/dist/documents/repository.d.ts.map +1 -0
  505. package/dist/documents/repository.js +236 -0
  506. package/dist/documents/repository.js.map +1 -0
  507. package/dist/documents/requests.d.ts +73 -0
  508. package/dist/documents/requests.d.ts.map +1 -0
  509. package/dist/documents/requests.js +127 -0
  510. package/dist/documents/requests.js.map +1 -0
  511. package/dist/index.d.ts +113 -13
  512. package/dist/index.d.ts.map +1 -1
  513. package/dist/index.js +104 -8
  514. package/dist/index.js.map +1 -1
  515. package/dist/legal/company.d.ts +32 -0
  516. package/dist/legal/company.d.ts.map +1 -0
  517. package/dist/legal/company.js +33 -0
  518. package/dist/legal/company.js.map +1 -0
  519. package/dist/legal/documents.d.ts +30 -0
  520. package/dist/legal/documents.d.ts.map +1 -0
  521. package/dist/legal/documents.js +666 -0
  522. package/dist/legal/documents.js.map +1 -0
  523. package/dist/legal/index.d.ts +5 -0
  524. package/dist/legal/index.d.ts.map +1 -0
  525. package/dist/legal/index.js +4 -0
  526. package/dist/legal/index.js.map +1 -0
  527. package/dist/legal/types.d.ts +145 -0
  528. package/dist/legal/types.d.ts.map +1 -0
  529. package/dist/legal/types.js +31 -0
  530. package/dist/legal/types.js.map +1 -0
  531. package/dist/mail/auth-templates.d.ts +15 -0
  532. package/dist/mail/auth-templates.d.ts.map +1 -0
  533. package/dist/mail/auth-templates.js +26 -0
  534. package/dist/mail/auth-templates.js.map +1 -0
  535. package/dist/mail/dedup-store.d.ts +1 -24
  536. package/dist/mail/dedup-store.d.ts.map +1 -1
  537. package/dist/mail/dedup-store.js +9 -48
  538. package/dist/mail/dedup-store.js.map +1 -1
  539. package/dist/mail/index.d.ts +2 -1
  540. package/dist/mail/index.d.ts.map +1 -1
  541. package/dist/mail/index.js +13 -2
  542. package/dist/mail/index.js.map +1 -1
  543. package/dist/mail/provider.d.ts +2 -30
  544. package/dist/mail/provider.d.ts.map +1 -1
  545. package/dist/mail/provider.js +10 -246
  546. package/dist/mail/provider.js.map +1 -1
  547. package/dist/mail/resend.d.ts +2 -12
  548. package/dist/mail/resend.d.ts.map +1 -1
  549. package/dist/mail/resend.js +11 -17
  550. package/dist/mail/resend.js.map +1 -1
  551. package/dist/mail/sender-resolver.d.ts +1 -7
  552. package/dist/mail/sender-resolver.d.ts.map +1 -1
  553. package/dist/mail/sender-resolver.js +11 -10
  554. package/dist/mail/sender-resolver.js.map +1 -1
  555. package/dist/mail/template-renderer.d.ts +1 -9
  556. package/dist/mail/template-renderer.d.ts.map +1 -1
  557. package/dist/mail/template-renderer.js +10 -15
  558. package/dist/mail/template-renderer.js.map +1 -1
  559. package/dist/mail/transactional-outbound.d.ts +115 -0
  560. package/dist/mail/transactional-outbound.d.ts.map +1 -0
  561. package/dist/mail/transactional-outbound.js +113 -0
  562. package/dist/mail/transactional-outbound.js.map +1 -0
  563. package/dist/mail/types.d.ts +2 -142
  564. package/dist/mail/types.d.ts.map +1 -1
  565. package/dist/mail/types.js +1 -33
  566. package/dist/mail/types.js.map +1 -1
  567. package/dist/portal/provisioning.d.ts +58 -0
  568. package/dist/portal/provisioning.d.ts.map +1 -0
  569. package/dist/portal/provisioning.js +139 -0
  570. package/dist/portal/provisioning.js.map +1 -0
  571. package/dist/pricing-engine/canonical.d.ts +14 -0
  572. package/dist/pricing-engine/canonical.d.ts.map +1 -0
  573. package/dist/pricing-engine/canonical.js +39 -0
  574. package/dist/pricing-engine/canonical.js.map +1 -0
  575. package/dist/pricing-engine/errors.d.ts.map +1 -1
  576. package/dist/pricing-engine/errors.js +6 -1
  577. package/dist/pricing-engine/errors.js.map +1 -1
  578. package/dist/pricing-engine/index.d.ts +12 -2
  579. package/dist/pricing-engine/index.d.ts.map +1 -1
  580. package/dist/pricing-engine/index.js +6 -1
  581. package/dist/pricing-engine/index.js.map +1 -1
  582. package/dist/pricing-engine/pipeline.d.ts.map +1 -1
  583. package/dist/pricing-engine/pipeline.js +202 -28
  584. package/dist/pricing-engine/pipeline.js.map +1 -1
  585. package/dist/pricing-engine/shadow-adapter.d.ts +37 -0
  586. package/dist/pricing-engine/shadow-adapter.d.ts.map +1 -0
  587. package/dist/pricing-engine/shadow-adapter.js +75 -0
  588. package/dist/pricing-engine/shadow-adapter.js.map +1 -0
  589. package/dist/pricing-engine/shadow-diff.d.ts +26 -0
  590. package/dist/pricing-engine/shadow-diff.d.ts.map +1 -0
  591. package/dist/pricing-engine/shadow-diff.js +52 -0
  592. package/dist/pricing-engine/shadow-diff.js.map +1 -0
  593. package/dist/pricing-engine/shadow-facade.d.ts +42 -0
  594. package/dist/pricing-engine/shadow-facade.d.ts.map +1 -0
  595. package/dist/pricing-engine/shadow-facade.js +82 -0
  596. package/dist/pricing-engine/shadow-facade.js.map +1 -0
  597. package/dist/pricing-engine/shadow-legacy-types.d.ts +64 -0
  598. package/dist/pricing-engine/shadow-legacy-types.d.ts.map +1 -0
  599. package/dist/pricing-engine/shadow-legacy-types.js +18 -0
  600. package/dist/pricing-engine/shadow-legacy-types.js.map +1 -0
  601. package/dist/pricing-engine/shadow-metrics.d.ts +33 -0
  602. package/dist/pricing-engine/shadow-metrics.d.ts.map +1 -0
  603. package/dist/pricing-engine/shadow-metrics.js +31 -0
  604. package/dist/pricing-engine/shadow-metrics.js.map +1 -0
  605. package/dist/pricing-engine/shadow-normalizer.d.ts +17 -0
  606. package/dist/pricing-engine/shadow-normalizer.d.ts.map +1 -0
  607. package/dist/pricing-engine/shadow-normalizer.js +39 -0
  608. package/dist/pricing-engine/shadow-normalizer.js.map +1 -0
  609. package/dist/pricing-engine/types.d.ts +2 -0
  610. package/dist/pricing-engine/types.d.ts.map +1 -1
  611. package/dist/rbac/authorize.d.ts +12 -0
  612. package/dist/rbac/authorize.d.ts.map +1 -1
  613. package/dist/rbac/authorize.js +27 -0
  614. package/dist/rbac/authorize.js.map +1 -1
  615. package/dist/rbac/catalog.d.ts.map +1 -1
  616. package/dist/rbac/catalog.js +67 -1
  617. package/dist/rbac/catalog.js.map +1 -1
  618. package/dist/rbac/workload-provisioning.d.ts +66 -0
  619. package/dist/rbac/workload-provisioning.d.ts.map +1 -0
  620. package/dist/rbac/workload-provisioning.js +153 -0
  621. package/dist/rbac/workload-provisioning.js.map +1 -0
  622. package/dist/rbac/workload.d.ts +124 -0
  623. package/dist/rbac/workload.d.ts.map +1 -0
  624. package/dist/rbac/workload.js +184 -0
  625. package/dist/rbac/workload.js.map +1 -0
  626. package/dist/storage/checksum.d.ts +9 -0
  627. package/dist/storage/checksum.d.ts.map +1 -0
  628. package/dist/storage/checksum.js +12 -0
  629. package/dist/storage/checksum.js.map +1 -0
  630. package/dist/storage/key.d.ts +37 -0
  631. package/dist/storage/key.d.ts.map +1 -0
  632. package/dist/storage/key.js +67 -0
  633. package/dist/storage/key.js.map +1 -0
  634. package/dist/storage/local.d.ts +40 -0
  635. package/dist/storage/local.d.ts.map +1 -0
  636. package/dist/storage/local.js +96 -0
  637. package/dist/storage/local.js.map +1 -0
  638. package/dist/storage/r2.d.ts +43 -0
  639. package/dist/storage/r2.d.ts.map +1 -0
  640. package/dist/storage/r2.js +177 -0
  641. package/dist/storage/r2.js.map +1 -0
  642. package/dist/storage/registry.d.ts +23 -0
  643. package/dist/storage/registry.d.ts.map +1 -0
  644. package/dist/storage/registry.js +37 -0
  645. package/dist/storage/registry.js.map +1 -0
  646. package/dist/storage/types.d.ts +67 -0
  647. package/dist/storage/types.d.ts.map +1 -0
  648. package/dist/storage/types.js +45 -0
  649. package/dist/storage/types.js.map +1 -0
  650. package/dist/subscription-billing/provider/asaas.d.ts +14 -0
  651. package/dist/subscription-billing/provider/asaas.d.ts.map +1 -1
  652. package/dist/subscription-billing/provider/asaas.js +32 -7
  653. package/dist/subscription-billing/provider/asaas.js.map +1 -1
  654. package/dist/subscription-billing/provider/disabled.d.ts +9 -0
  655. package/dist/subscription-billing/provider/disabled.d.ts.map +1 -1
  656. package/dist/subscription-billing/provider/disabled.js +8 -0
  657. package/dist/subscription-billing/provider/disabled.js.map +1 -1
  658. package/dist/subscription-billing/provider/registry.d.ts +10 -0
  659. package/dist/subscription-billing/provider/registry.d.ts.map +1 -1
  660. package/dist/subscription-billing/provider/registry.js +17 -0
  661. package/dist/subscription-billing/provider/registry.js.map +1 -1
  662. package/dist/subscription-billing/service.d.ts +17 -1
  663. package/dist/subscription-billing/service.d.ts.map +1 -1
  664. package/dist/subscription-billing/service.js +57 -8
  665. package/dist/subscription-billing/service.js.map +1 -1
  666. package/dist/subscription-billing/types.d.ts +13 -0
  667. package/dist/subscription-billing/types.d.ts.map +1 -1
  668. package/dist/subscription-billing/types.js.map +1 -1
  669. package/dist/tenancy/provisioning.d.ts.map +1 -1
  670. package/dist/tenancy/provisioning.js +32 -8
  671. package/dist/tenancy/provisioning.js.map +1 -1
  672. package/dist/users/repository.d.ts +38 -0
  673. package/dist/users/repository.d.ts.map +1 -1
  674. package/dist/users/repository.js +118 -3
  675. package/dist/users/repository.js.map +1 -1
  676. package/dist/vault/repository.d.ts +67 -0
  677. package/dist/vault/repository.d.ts.map +1 -1
  678. package/dist/vault/repository.js +78 -0
  679. package/dist/vault/repository.js.map +1 -1
  680. package/package.json +19 -4
  681. package/src/db/migrations/0023_user_invite_activation.sql +15 -0
  682. package/src/db/migrations/0024_set_user_password_function.sql +32 -0
  683. package/src/db/migrations/0025_revoke_user_session_function.sql +28 -0
  684. package/src/db/migrations/0026_documents_and_competence.sql +353 -0
  685. package/src/db/migrations/0027_receivable_client_isolation.sql +58 -0
  686. package/src/db/migrations/0028_collection_engine.sql +94 -0
  687. package/src/db/migrations/0029_payment_rail.sql +107 -0
  688. package/src/db/migrations/0030_provider_account_uniqueness.sql +22 -0
  689. package/src/db/migrations/0031_provider_account_environment_scope.sql +21 -0
  690. package/src/db/migrations/0032_settlement_reversal.sql +76 -0
  691. package/src/db/migrations/0033_receivable_idempotency.sql +28 -0
  692. package/src/db/migrations/0034_payment_customer_reconcile.sql +30 -0
  693. package/src/db/migrations/0035_payment_account_environments.sql +61 -0
  694. package/src/db/migrations/0036_primary_backfill_under_force_rls.sql +41 -0
  695. package/src/db/migrations/0037_receivable_generation_source.sql +29 -0
  696. package/src/db/migrations/0038_notification_channels.sql +175 -0
  697. package/src/db/migrations/0039_communication_dispatch_claim.sql +193 -0
  698. package/src/db/migrations/0040_communication_unknown_outcome.sql +99 -0
  699. package/src/db/migrations/0041_channel_binding_routing.sql +146 -0
  700. package/src/db/migrations/0042_channel_route_read_isolation.sql +165 -0
  701. package/src/db/migrations/0043_workload_identity.sql +105 -0
  702. package/src/db/migrations/0044_channel_onboarding_session.sql +96 -0
  703. package/dist/cjs/mail/resend-client.js +0 -103
  704. package/dist/cjs/mail/resend-client.js.map +0 -1
  705. package/dist/mail/resend-client.d.ts +0 -16
  706. package/dist/mail/resend-client.d.ts.map +0 -1
  707. package/dist/mail/resend-client.js +0 -100
  708. package/dist/mail/resend-client.js.map +0 -1
@@ -0,0 +1,99 @@
1
+ -- 0040 — Desfecho desconhecido, marca de chamada externa e trilha durável
2
+ --
3
+ -- A 0039 fechou a janela do envio duplo sob concorrência: a reserva é tomada
4
+ -- ANTES do efeito externo, numa instrução atômica. O que ela NÃO fechou, e a
5
+ -- segunda certificação independente apontou, é o que acontece quando o titular
6
+ -- da reserva morre.
7
+ --
8
+ -- A reserva tem prazo (10 min) porque processo morre, e uma etapa calada para
9
+ -- sempre é pior que uma duplicata rara. Mas a retomada por prazo vencido era
10
+ -- CEGA: o motor não tinha como distinguir
11
+ --
12
+ -- (a) o titular morreu ANTES de chamar o provider — nada saiu, reenviar é certo;
13
+ -- (b) o titular morreu DEPOIS de chamar o provider — pode ter saído, reenviar duplica.
14
+ --
15
+ -- Os dois chegavam com a mesma cara, e o motor reenviava nos dois. Essa migration
16
+ -- dá ao banco o que falta para separá-los, e um estado para o caso em que a
17
+ -- separação não basta.
18
+
19
+ -- ── A marca da chamada externa ──────────────────────────────────────────────
20
+ --
21
+ -- Gravada IMEDIATAMENTE antes de falar com o provider, em transação própria e
22
+ -- commitada. Precisa sobreviver à morte do processo que a escreveu — se vivesse
23
+ -- na transação de quem chama, ficaria invisível justamente no caso que ela
24
+ -- existe para cobrir, porque o processo que morre é o que nunca commita.
25
+ --
26
+ -- Erra para o lado seguro por construção: quem morre ENTRE a marca e a chamada
27
+ -- é classificado como "pode ter enviado" sem ter enviado. Isso cala uma
28
+ -- mensagem; a alternativa manda duas.
29
+ ALTER TABLE communication_dispatch
30
+ ADD COLUMN IF NOT EXISTS provider_attempts integer NOT NULL DEFAULT 0;
31
+
32
+ -- Instante da PRIMEIRA chamada externa desta chave. A janela de idempotência do
33
+ -- provider conta a partir dela, não da tentativa atual: uma chave que o provider
34
+ -- já esqueceu não protege mais nada, e reenviar depois disso é uma mensagem nova.
35
+ ALTER TABLE communication_dispatch
36
+ ADD COLUMN IF NOT EXISTS provider_first_attempt_at timestamptz;
37
+
38
+ -- Última chamada externa. Diagnóstico operacional — responde "há quanto tempo
39
+ -- esta reserva está pendurada depois de já ter falado com o provider".
40
+ ALTER TABLE communication_dispatch
41
+ ADD COLUMN IF NOT EXISTS provider_last_attempt_at timestamptz;
42
+
43
+ -- ── O estado que faltava ────────────────────────────────────────────────────
44
+ --
45
+ -- `unknown_outcome`: alguém chamou o provider e ninguém sabe o que aconteceu.
46
+ --
47
+ -- Não é falha (pode ter dado certo) e não é sucesso (pode não ter saído). Os
48
+ -- quatro estados anteriores obrigavam a mentir: `failed_retryable` devolveria a
49
+ -- chave e a próxima rodada duplicaria; `sent` afirmaria uma entrega que ninguém
50
+ -- viu.
51
+ --
52
+ -- A chave fica travada aqui de propósito. Destravar sozinha é escolher, no
53
+ -- escuro, entre calar um aviso e cobrar duas vezes — e essa escolha é de quem
54
+ -- consegue olhar o painel do provider, não do cron.
55
+ ALTER TABLE communication_dispatch
56
+ DROP CONSTRAINT IF EXISTS communication_dispatch_status_check;
57
+
58
+ ALTER TABLE communication_dispatch
59
+ ADD CONSTRAINT communication_dispatch_status_check
60
+ CHECK (status IN ('in_flight', 'sent', 'failed_retryable', 'failed_permanent', 'unknown_outcome'));
61
+
62
+ -- Reservas paradas em desfecho desconhecido, para a fila de quem investiga.
63
+ -- Parcial porque a expectativa é que sejam raras: um índice cheio sobre um
64
+ -- estado excepcional é um índice que só custa.
65
+ CREATE INDEX IF NOT EXISTS communication_dispatch_unknown_idx
66
+ ON communication_dispatch (tenant_id, updated_at DESC)
67
+ WHERE status = 'unknown_outcome';
68
+
69
+ -- ── Trilha durável ──────────────────────────────────────────────────────────
70
+ --
71
+ -- `recipient_ref` passa a viver na reserva porque a trilha do CICLO DE VIDA do
72
+ -- disparo (`dispatch_claimed`, `dispatch_sent`, `dispatch_failed`) passou a ser
73
+ -- escrita na mesma transação da reserva, e não mais na transação de negócio de
74
+ -- quem chamou.
75
+ --
76
+ -- O defeito que isso corrige é concreto. A reserva commita em transação própria
77
+ -- e curta (precisa ficar visível a outros workers antes do fim do ciclo); a
78
+ -- trilha vivia na transação longa da régua. Um rollback da régua DEPOIS de a
79
+ -- mensagem sair produzia:
80
+ --
81
+ -- communication_dispatch.status = 'sent' (sobreviveu — a mensagem saiu)
82
+ -- communication_event.dispatch_sent (sumiu — a narrativa foi desfeita)
83
+ --
84
+ -- Um disparo enviado sem nenhuma linha explicando que foi enviado. Escrevendo as
85
+ -- duas coisas juntas, elas caem ou sobrevivem juntas. Não é outbox nem
86
+ -- dual-write com compensação: é uma escrita só.
87
+ --
88
+ -- Referência LÓGICA (id do cliente), como no resto do motor. NUNCA telefone,
89
+ -- NUNCA e-mail: a reserva é consultada por quem opera, e uma reserva que carrega
90
+ -- contato transforma toda consulta operacional numa exportação de PII.
91
+ ALTER TABLE communication_dispatch
92
+ ADD COLUMN IF NOT EXISTS recipient_ref text;
93
+
94
+ COMMENT ON COLUMN communication_dispatch.provider_attempts IS
95
+ 'Quantas vezes o provider foi chamado por esta chave. Marcado ANTES da chamada, em transação própria.';
96
+ COMMENT ON COLUMN communication_dispatch.provider_first_attempt_at IS
97
+ 'Primeira chamada externa. A janela de idempotência do provider conta a partir daqui.';
98
+ COMMENT ON COLUMN communication_dispatch.recipient_ref IS
99
+ 'Referência lógica do destinatário, para a trilha durável. Nunca telefone, nunca e-mail.';
@@ -0,0 +1,146 @@
1
+ -- 0041 — Vínculo de canal por tenant e roteamento reverso de webhook
2
+ --
3
+ -- O que muda de premissa. Até aqui, a configuração de canal do escritório vivia
4
+ -- em `tenant.settings.channels` (jsonb) e o webhook trazia o tenant no PATH da
5
+ -- URL. Isso é coerente com uma topologia em que cada tenant tem o próprio Meta
6
+ -- App e o próprio webhook — e incoerente com a topologia que o motor vai
7
+ -- operar, em que existe UM App do motor e N contas de clientes.
8
+ --
9
+ -- Sob um App único, a URL de callback e o verify token pertencem ao APP. A Meta
10
+ -- entrega os eventos de todas as contas assinadas no mesmo endereço e não tem
11
+ -- como saber o nosso `tenantId`. O tenant precisa sair do PAYLOAD, pelo
12
+ -- `phone_number_id` — e isso exige um caminho de leitura que vai da conta do
13
+ -- provider para o tenant, exatamente o inverso do que existe hoje.
14
+ --
15
+ -- Nada aqui apaga o caminho anterior: `tenant.settings.channels` continua
16
+ -- válido e continua sendo lido. Esta migração acrescenta o registro canônico e
17
+ -- o índice de roteamento.
18
+
19
+ -- ── Vínculo de canal ────────────────────────────────────────────────────────
20
+ --
21
+ -- Uma linha por (tenant, produto, marca, canal). É o que `resolveIdentity` já
22
+ -- pressupõe: o motor pergunta "qual a conta DESTE contexto neste canal" e
23
+ -- espera no máximo uma resposta.
24
+ --
25
+ -- `credential_ref` é referência LÓGICA — o nome da credencial no Vault Engine
26
+ -- (`0012_vault.sql`), nunca o valor. O ciclo de vida do segredo (vencimento,
27
+ -- rotação, remoção) já é do Vault e não é duplicado aqui: duas fontes para
28
+ -- "quando este token expira" divergem no dia em que uma é atualizada e a outra
29
+ -- não.
30
+ CREATE TABLE IF NOT EXISTS channel_binding (
31
+ id uuid PRIMARY KEY,
32
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
33
+ -- Produto e marca fazem parte da identidade do vínculo porque fazem parte do
34
+ -- `CommunicationContext`. Um tenant que consome dois produtos do ecossistema
35
+ -- tem duas conversas distintas com o cliente dele.
36
+ product text NOT NULL,
37
+ brand text NOT NULL,
38
+ -- Só canais de TELEFONE. E-mail não tem conta de provider por tenant no
39
+ -- desenho atual (o fallback de plataforma existe e é declaradamente
40
+ -- email-only), e criar a linha para ele daria a impressão de que tem.
41
+ channel text NOT NULL CHECK (channel IN ('whatsapp', 'sms')),
42
+ provider text NOT NULL CHECK (provider IN ('meta_whatsapp')),
43
+ -- Identidade visível ao destinatário (E.164). Não é segredo — aparece na tela
44
+ -- e na trilha.
45
+ sender_id text NOT NULL,
46
+ display_name text,
47
+ -- Nome da credencial no Vault. Jamais o segredo.
48
+ credential_ref text NOT NULL,
49
+ -- `pending_validation` existe porque uma conta cadastrada e não verificada
50
+ -- parece pronta na tela e é recusada pelo provider. O operador merece ver a
51
+ -- diferença antes da primeira cobrança, não depois.
52
+ status text NOT NULL DEFAULT 'not_configured'
53
+ CHECK (status IN ('not_configured','pending_validation','active','paused','error')),
54
+ -- Revogação do VÍNCULO — distinta da revogação da credencial, que é do Vault.
55
+ -- Um tenant pode retirar o acesso do motor à conta dele sem que o segredo
56
+ -- deixe de existir.
57
+ revoked_at timestamptz,
58
+ created_at timestamptz NOT NULL DEFAULT now(),
59
+ updated_at timestamptz NOT NULL DEFAULT now(),
60
+ CONSTRAINT channel_binding_tenant_id_uq UNIQUE (tenant_id, id)
61
+ );
62
+
63
+ -- Um vínculo vivo por contexto e canal. Sem isto, duas linhas concorrentes
64
+ -- fariam `resolveIdentity` escolher uma — e escolher errado significa enviar
65
+ -- pela conta errada.
66
+ CREATE UNIQUE INDEX IF NOT EXISTS channel_binding_context_uq
67
+ ON channel_binding (tenant_id, product, brand, channel)
68
+ WHERE revoked_at IS NULL;
69
+
70
+ ALTER TABLE channel_binding ENABLE ROW LEVEL SECURITY;
71
+ ALTER TABLE channel_binding FORCE ROW LEVEL SECURITY;
72
+
73
+ CREATE POLICY channel_binding_tenant_isolation ON channel_binding
74
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
75
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
76
+
77
+ -- ── Índice de roteamento ────────────────────────────────────────────────────
78
+ --
79
+ -- Por que uma tabela separada, e não colunas em `channel_binding`.
80
+ --
81
+ -- A pergunta do webhook é "de quem é este `phone_number_id`?", feita ANTES de
82
+ -- existir contexto de tenant — não há como setar `app.current_tenant` para
83
+ -- descobrir qual é o tenant. Sob a RLS FORCE de `channel_binding`, essa
84
+ -- consulta não enxergaria linha nenhuma.
85
+ --
86
+ -- E há um segundo motivo, mais duro: a unicidade global. Se o par
87
+ -- (provider, conta) morasse numa tabela com RLS por tenant, um índice único
88
+ -- `(tenant_id, ...)` deixaria DOIS tenants reivindicarem o mesmo número, e
89
+ -- nenhum guard de aplicação pegaria isso — sob RLS, o guard do tenant A não
90
+ -- enxerga a linha do tenant B para comparar. A chave primária abaixo é global
91
+ -- de propósito: é o banco, e não o código, que garante que um número pertence a
92
+ -- no máximo um tenant.
93
+ --
94
+ -- O que esta tabela contém: identificadores emitidos pelo provider (que o
95
+ -- próprio provider manda em texto claro em todo webhook) e o dono. Nenhuma
96
+ -- credencial, nenhum dado de cliente, nenhuma PII.
97
+ CREATE TABLE IF NOT EXISTS channel_route (
98
+ provider text NOT NULL CHECK (provider IN ('meta_whatsapp')),
99
+ -- O identificador de ROTEAMENTO. Na Meta, `phone_number_id`.
100
+ provider_account_ref text NOT NULL,
101
+ -- O agrupador (WABA). Conferência de coerência, nunca segunda chave.
102
+ provider_parent_ref text,
103
+ tenant_id uuid NOT NULL,
104
+ binding_id uuid NOT NULL,
105
+ created_at timestamptz NOT NULL DEFAULT now(),
106
+ updated_at timestamptz NOT NULL DEFAULT now(),
107
+ -- GLOBAL, não por tenant. Ver o comentário acima.
108
+ PRIMARY KEY (provider, provider_account_ref),
109
+ -- A FK composta amarra a rota ao vínculo do MESMO tenant. Não há como
110
+ -- registrar uma rota apontando para o vínculo de outro escritório.
111
+ FOREIGN KEY (tenant_id, binding_id)
112
+ REFERENCES channel_binding (tenant_id, id) ON DELETE CASCADE
113
+ );
114
+
115
+ CREATE INDEX IF NOT EXISTS channel_route_tenant_idx ON channel_route (tenant_id);
116
+
117
+ ALTER TABLE channel_route ENABLE ROW LEVEL SECURITY;
118
+ ALTER TABLE channel_route FORCE ROW LEVEL SECURITY;
119
+
120
+ -- LEITURA: aberta apenas quando NÃO há tenant na sessão — que é o único momento
121
+ -- em que o roteador precisa consultar, e o único em que ele pode. Dentro de um
122
+ -- contexto de tenant, um escritório continua vendo só as próprias rotas.
123
+ --
124
+ -- O ramo aberto é uma decisão consciente e é o preço da topologia de App único.
125
+ -- O que ele expõe a um código que rode sem GUC é o mapa
126
+ -- (número do provider → tenant) — sem segredo, sem PII, sem conteúdo. O que ele
127
+ -- NÃO abre é escrita: veja a política seguinte.
128
+ CREATE POLICY channel_route_read ON channel_route FOR SELECT
129
+ USING (
130
+ NULLIF(current_setting('app.current_tenant', true), '') IS NULL
131
+ OR tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
132
+ );
133
+
134
+ -- ESCRITA: sempre fechada. Registrar ou remover rota exige estar dentro do
135
+ -- tenant dono. Sem isto, o mesmo ramo aberto da leitura permitiria a qualquer
136
+ -- código sem contexto reivindicar o número de um escritório.
137
+ CREATE POLICY channel_route_write ON channel_route FOR ALL
138
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
139
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
140
+
141
+ COMMENT ON TABLE channel_binding IS
142
+ 'Conta de canal de um tenant. credential_ref é o NOME no Vault, nunca o valor.';
143
+ COMMENT ON TABLE channel_route IS
144
+ 'Índice de roteamento de webhook. Chave primária GLOBAL: um phone_number_id pertence a no máximo um tenant.';
145
+ COMMENT ON COLUMN channel_route.provider_parent_ref IS
146
+ 'WABA. Conferência de coerência; nunca decide sozinho de quem é o evento.';
@@ -0,0 +1,165 @@
1
+ -- 0042 — Fecha a leitura de `channel_route` e move o roteamento para um caminho
2
+ -- de sistema explícito.
3
+ --
4
+ -- POR QUE UMA MIGRATION NOVA, E NÃO UMA CORREÇÃO NA 0041.
5
+ --
6
+ -- O runner (`db/migrate.ts`) guarda o checksum de cada arquivo aplicado e
7
+ -- recusa a subida quando ele muda: "Migration já aplicada foi modificada —
8
+ -- migrations são imutáveis, crie uma nova". Editar a 0041 faria o caminho de
9
+ -- UPGRADE (banco que já a aplicou) abortar antes de qualquer correção chegar,
10
+ -- e só o caminho FRESH veria a versão nova. Um defeito de segurança que se
11
+ -- corrige apenas em banco recriado não está corrigido.
12
+ --
13
+ -- O QUE A 0041 DEIXOU ABERTO.
14
+ --
15
+ -- A política de leitura dela era:
16
+ --
17
+ -- current_tenant IS NULL OR tenant_id = current_tenant
18
+ --
19
+ -- O primeiro ramo transformava a AUSÊNCIA de contexto em autorização. Isso
20
+ -- inverte a regra da casa: sem contexto, não se lê tabela de tenant. E a
21
+ -- ausência de contexto não é um estado raro — é o estado de qualquer código
22
+ -- que esqueceu de abrir `withContext`, de qualquer script, de qualquer job.
23
+ -- O ramo entregava o mapa (conta do provider → tenant) a todos eles, e a
24
+ -- listagem inteira: `SELECT * FROM channel_route` sem GUC devolvia o portfólio
25
+ -- de clientes do motor.
26
+ --
27
+ -- A necessidade que justificava o ramo continua real: o webhook do App único
28
+ -- precisa descobrir o tenant ANTES de poder abrir contexto dele. O que muda é
29
+ -- que essa necessidade passa a ser atendida por um caminho nomeado, com
30
+ -- assinatura fixa e resposta de no máximo uma linha, em vez de por um buraco na
31
+ -- política que qualquer consulta atravessa.
32
+
33
+ -- ── 1 · Leitura fecha ───────────────────────────────────────────────────────
34
+ --
35
+ -- Sem GUC, `NULLIF(...)::uuid` é NULL, e `tenant_id = NULL` é NULL — que a RLS
36
+ -- trata como falso. Fail-closed sem ramo especial.
37
+ DROP POLICY IF EXISTS channel_route_read ON channel_route;
38
+
39
+ CREATE POLICY channel_route_read ON channel_route FOR SELECT
40
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
41
+
42
+ -- ── 2 · O caminho de sistema, explícito ─────────────────────────────────────
43
+ --
44
+ -- `channel_route_resolve` é a ÚNICA forma de perguntar "de quem é esta conta"
45
+ -- sem contexto de tenant, e ela responde só isso:
46
+ --
47
+ -- - exige a chave exata (provider + conta). Não existe consulta por prefixo,
48
+ -- por tenant, por faixa, nem sem argumento: não há como enumerar o mapa.
49
+ -- - devolve no máximo UMA linha, e só as colunas de roteamento.
50
+ -- - `SECURITY DEFINER` com `search_path` fixo — sem isso, um schema no
51
+ -- caminho de busca do chamador poderia sombrear `channel_route` e a função
52
+ -- leria a tabela de outra pessoa com o privilégio do dono.
53
+ -- - `STABLE`, nunca `VOLATILE`: não escreve.
54
+ --
55
+ -- O privilégio de execução é concedido nominalmente ao papel de aplicação. O
56
+ -- `REVOKE ... FROM PUBLIC` é obrigatório porque o Postgres concede EXECUTE a
57
+ -- PUBLIC por padrão em toda função criada — sem ele, "estritamente limitada"
58
+ -- seria só o nome.
59
+ CREATE OR REPLACE FUNCTION channel_route_resolve(
60
+ p_provider text,
61
+ p_account_ref text
62
+ )
63
+ RETURNS TABLE (
64
+ provider text,
65
+ provider_account_ref text,
66
+ provider_parent_ref text,
67
+ tenant_id uuid,
68
+ binding_id uuid
69
+ )
70
+ LANGUAGE sql
71
+ STABLE
72
+ SECURITY DEFINER
73
+ SET search_path = pg_catalog, public
74
+ AS $fn$
75
+ SELECT r.provider, r.provider_account_ref, r.provider_parent_ref,
76
+ r.tenant_id, r.binding_id
77
+ FROM public.channel_route r
78
+ WHERE r.provider = p_provider
79
+ AND r.provider_account_ref = p_account_ref
80
+ LIMIT 1
81
+ $fn$;
82
+
83
+ REVOKE ALL ON FUNCTION channel_route_resolve(text, text) FROM PUBLIC;
84
+
85
+ DO $$
86
+ BEGIN
87
+ IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'baruk_app') THEN
88
+ EXECUTE 'GRANT EXECUTE ON FUNCTION channel_route_resolve(text, text) TO baruk_app';
89
+ END IF;
90
+ END $$;
91
+
92
+ -- ── 3 · Por que a função consegue ler o que o chamador não consegue ─────────
93
+ --
94
+ -- `channel_route` tem FORCE ROW LEVEL SECURITY, e FORCE aplica as políticas
95
+ -- TAMBÉM ao dono da tabela. Sem uma política que o autorize, a função
96
+ -- `SECURITY DEFINER` — que roda com a identidade do dono — leria zero linhas, e
97
+ -- o roteamento morreria em silêncio.
98
+ --
99
+ -- A autorização é dada nominalmente ao papel que é dono do schema (o mesmo que
100
+ -- roda migrations), e é de LEITURA APENAS. A escrita continua sujeita à
101
+ -- política por tenant inclusive para ele: `channel_route_write` não foi
102
+ -- afetada, e nenhuma política nova concede INSERT/UPDATE/DELETE.
103
+ --
104
+ -- O nome do papel varia por ambiente (`baruk_test` local, outro em produção),
105
+ -- então ele é descoberto — não escrito à mão.
106
+ DO $$
107
+ DECLARE
108
+ owner_role text;
109
+ BEGIN
110
+ SELECT tableowner INTO owner_role
111
+ FROM pg_tables
112
+ WHERE schemaname = 'public' AND tablename = 'channel_route';
113
+
114
+ IF owner_role IS NULL THEN
115
+ RAISE EXCEPTION 'channel_route sem dono identificável — abortando';
116
+ END IF;
117
+
118
+ EXECUTE format('DROP POLICY IF EXISTS channel_route_system_resolver ON channel_route');
119
+ EXECUTE format(
120
+ 'CREATE POLICY channel_route_system_resolver ON channel_route FOR SELECT TO %I USING (true)',
121
+ owner_role
122
+ );
123
+ END $$;
124
+
125
+ COMMENT ON FUNCTION channel_route_resolve(text, text) IS
126
+ 'Resolução de webhook fora de contexto de tenant. Chave exata, uma linha, sem enumeração.';
127
+
128
+ -- ── 4 · A credencial de canal de telefone tem de ser do COFRE ───────────────
129
+ --
130
+ -- `isAcceptableForNewBinding` já dizia isto em TypeScript, e nenhum caminho de
131
+ -- escrita a chamava. A 0042 corrige o caminho (ver `registerChannelBinding`) e
132
+ -- repete a regra aqui, porque um guard de aplicação cobre a função que o
133
+ -- invoca — não cobre um `UPDATE` cru, uma importação, um script de correção de
134
+ -- dados nem a próxima função de escrita que alguém escrever.
135
+ --
136
+ -- Um `credential_ref` que não começa por `vault://` num canal de telefone
137
+ -- significa um token de ambiente, e um token de ambiente é COMPARTILHADO: o
138
+ -- bloqueio que a Meta aplicar a um escritório cai sobre todos os outros.
139
+ --
140
+ -- `NOT VALID` de propósito, e é exatamente a compatibilidade legada do §10.
141
+ -- Ele NÃO afrouxa a regra para escritas: todo INSERT e todo UPDATE passam pela
142
+ -- verificação, inclusive um UPDATE numa linha antiga. O que ele dispensa é a
143
+ -- varredura das linhas que já existem — que hoje são zero (a tabela nasceu na
144
+ -- 0041, que nunca foi mesclada), e que num ambiente onde não fossem
145
+ -- continuariam legíveis e utilizáveis até serem tocadas.
146
+ --
147
+ -- Plano de remoção do `NOT VALID`: quando o inventário de `channel_binding`
148
+ -- confirmar zero linhas com referência de ambiente, uma migration futura roda
149
+ -- `VALIDATE CONSTRAINT` e a exceção deixa de existir.
150
+ DO $$
151
+ BEGIN
152
+ IF NOT EXISTS (
153
+ SELECT FROM pg_constraint WHERE conname = 'channel_binding_phone_credential_ref_chk'
154
+ ) THEN
155
+ ALTER TABLE channel_binding
156
+ ADD CONSTRAINT channel_binding_phone_credential_ref_chk
157
+ CHECK (
158
+ channel NOT IN ('whatsapp', 'sms')
159
+ OR credential_ref LIKE 'vault://%'
160
+ ) NOT VALID;
161
+ END IF;
162
+ END $$;
163
+
164
+ COMMENT ON CONSTRAINT channel_binding_phone_credential_ref_chk ON channel_binding IS
165
+ 'Canal de telefone exige credencial do cofre. NOT VALID cobre linhas legadas; escritas são verificadas.';
@@ -0,0 +1,105 @@
1
+ -- 0043 — Identidade e autorização de workload NÃO-HUMANO.
2
+ --
3
+ -- Por que uma tabela nova, e não `user`/`user_role`.
4
+ --
5
+ -- O RBAC existente resolve autorização de PESSOA: `user` → `membership` →
6
+ -- `user_role` → `role_permission` → `permission`. Reaproveitá-lo para um cron
7
+ -- exigiria criar uma linha em `"user"` — e `login()` (auth/service.ts) não
8
+ -- filtra `user_type`. Um principal de automação virando linha de `"user"`
9
+ -- ganharia, de graça, a superfície de login interativo: bastaria o fluxo de
10
+ -- convite/reset alcançar aquele e-mail para que um humano obtivesse uma sessão
11
+ -- HTTP carregando `vault.credential.decrypt`. Além disso, o menor papel de
12
+ -- sistema que carrega essa permissão é `accountant`, que carrega junto
13
+ -- `client.write`, `billing.write` e mais trinta — raio de dano incompatível
14
+ -- com "este processo lê UMA credencial de canal".
15
+ --
16
+ -- O que estas duas tabelas fazem é o mínimo: separar IDENTIDADE (global, como
17
+ -- `"user"`) de AUTORIZAÇÃO (tenant-scoped, como `user_role`), mantendo o
18
+ -- vocabulário de permissão no catálogo já existente (FK para `permission.key`)
19
+ -- e acrescentando os dois escopos que o RBAC de pessoa não tem — PROPÓSITO e
20
+ -- RECURSO. Sem eles, autorizar um workload a decifrar "alguma coisa" no tenant
21
+ -- A o autorizaria a decifrar TUDO do tenant A, e um `credentialRef` vindo de
22
+ -- input viraria confused deputy.
23
+ --
24
+ -- A trava que importa não está aqui, e sim nos privilégios: `baruk_app` recebe
25
+ -- apenas SELECT nestas duas tabelas (ver `db/migrate.ts` §3 e o bloco final
26
+ -- deste arquivo). O processo que CONSOME a autorização não consegue ESCREVÊ-LA.
27
+ -- Autoridade escrita pelo controlado não é autoridade.
28
+
29
+ -- ── service_principal (identidade global de workload — espelha `"user"`) ─────
30
+ CREATE TABLE service_principal (
31
+ id uuid PRIMARY KEY,
32
+ -- Slug estável, é o que a raiz de composição apresenta ('collection-cron').
33
+ key text NOT NULL,
34
+ display_name text NOT NULL,
35
+ status text NOT NULL DEFAULT 'active'
36
+ CHECK (status IN ('active', 'disabled')),
37
+ -- scrypt, mesmo formato de `user_credential.password_hash`. NUNCA o segredo.
38
+ -- O segredo em claro vive só no ambiente do deploy que roda aquele workload.
39
+ secret_hash text NOT NULL,
40
+ secret_rotated_at timestamptz,
41
+ created_at timestamptz NOT NULL DEFAULT now(),
42
+ updated_at timestamptz NOT NULL DEFAULT now(),
43
+ disabled_at timestamptz,
44
+ deleted_at timestamptz
45
+ );
46
+ CREATE UNIQUE INDEX service_principal_key_uq
47
+ ON service_principal (lower(key)) WHERE deleted_at IS NULL;
48
+
49
+ -- ── service_principal_grant (autorização tenant-scoped — espelha `user_role`) ─
50
+ -- Uma linha = "o principal P pode exercer a permissão K, no tenant T, para o
51
+ -- propósito R, sobre ESTE recurso". As quatro dimensões são conjuntivas.
52
+ CREATE TABLE service_principal_grant (
53
+ id uuid PRIMARY KEY,
54
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
55
+ principal_id uuid NOT NULL REFERENCES service_principal(id),
56
+ -- FK para o catálogo: a permissão de workload usa o MESMO vocabulário do
57
+ -- RBAC de pessoa. Uma permissão inventada não entra.
58
+ permission_key text NOT NULL REFERENCES permission(key),
59
+ -- Propósito declarado. Mesmo vocabulário fechado de `SecretAccessReason`.
60
+ purpose text NOT NULL
61
+ CHECK (purpose IN ('channel_dispatch', 'onboarding_seal', 'health_probe')),
62
+ -- Escopo de recurso. `exact` = um nome de credencial; `prefix` = um namespace
63
+ -- ('whatsapp/'). O comprimento mínimo existe para que `prefix = ''` — que
64
+ -- alcançaria tudo — não seja representável.
65
+ resource_kind text NOT NULL CHECK (resource_kind IN ('exact', 'prefix')),
66
+ resource_value text NOT NULL CHECK (length(btrim(resource_value)) >= 3),
67
+ -- Quem concedeu. Humano do tenant, ou NULL quando a concessão nasce do
68
+ -- provisionamento com a credencial de migração. Nunca o próprio principal.
69
+ granted_by uuid REFERENCES "user"(id),
70
+ granted_reason text,
71
+ created_at timestamptz NOT NULL DEFAULT now(),
72
+ -- Validade temporal opcional. NULL = vigente até revogação — deliberado: uma
73
+ -- concessão do cron de cobrança que vence sozinha vira parada de produção
74
+ -- silenciosa. A validade CURTA e obrigatória vive na sessão em memória
75
+ -- (`WorkloadIdentity.expiresAt`), onde expirar não custa disponibilidade.
76
+ expires_at timestamptz,
77
+ revoked_at timestamptz,
78
+ deleted_at timestamptz
79
+ );
80
+ CREATE UNIQUE INDEX service_principal_grant_uq
81
+ ON service_principal_grant (tenant_id, principal_id, permission_key, purpose,
82
+ resource_kind, lower(resource_value))
83
+ WHERE deleted_at IS NULL AND revoked_at IS NULL;
84
+ CREATE INDEX service_principal_grant_lookup_idx
85
+ ON service_principal_grant (tenant_id, principal_id, permission_key);
86
+
87
+ ALTER TABLE service_principal_grant ENABLE ROW LEVEL SECURITY;
88
+ ALTER TABLE service_principal_grant FORCE ROW LEVEL SECURITY;
89
+ CREATE POLICY service_principal_grant_tenant_isolation ON service_principal_grant
90
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
91
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
92
+
93
+ -- ── Privilégios: o app LÊ, nunca escreve ────────────────────────────────────
94
+ -- Repetido em `db/migrate.ts` §3 porque o bloco de grants roda DEPOIS das
95
+ -- migrations e concede INSERT/UPDATE/DELETE em ALL TABLES. Aqui o estado já
96
+ -- nasce certo; lá ele sobrevive ao re-grant.
97
+ DO $$
98
+ BEGIN
99
+ IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'baruk_app') THEN
100
+ EXECUTE 'GRANT SELECT ON service_principal TO baruk_app';
101
+ EXECUTE 'GRANT SELECT ON service_principal_grant TO baruk_app';
102
+ EXECUTE 'REVOKE INSERT, UPDATE, DELETE ON service_principal FROM baruk_app';
103
+ EXECUTE 'REVOKE INSERT, UPDATE, DELETE ON service_principal_grant FROM baruk_app';
104
+ END IF;
105
+ END $$;
@@ -0,0 +1,96 @@
1
+ -- 0044 — Sessão de onboarding de canal (Embedded Signup da Meta)
2
+ --
3
+ -- O que esta tabela existe para impedir.
4
+ --
5
+ -- O Embedded Signup devolve, NO NAVEGADOR do operador, um `code` trocável por
6
+ -- um token de negócio do cliente. Quem recebe esse `code` no servidor precisa
7
+ -- responder três perguntas antes de trocá-lo:
8
+ --
9
+ -- 1. este `code` veio de um fluxo que ESTE tenant iniciou? (CSRF)
10
+ -- 2. é a PRIMEIRA vez que ele chega? (replay)
11
+ -- 3. o fluxo começou há pouco? (janela)
12
+ --
13
+ -- Nenhuma delas é respondível pelo `code` sozinho: ele é opaco e a Meta não o
14
+ -- amarra a nada nosso. A resposta é um `state` que nós emitimos, guardamos e
15
+ -- consumimos uma única vez — o mesmo mecanismo do OAuth clássico, e pela mesma
16
+ -- razão.
17
+ --
18
+ -- ── Por que o HASH, e não o state ───────────────────────────────────────────
19
+ --
20
+ -- O `state` em claro vive no navegador e volta pela requisição. Guardá-lo em
21
+ -- claro faria desta tabela uma lista de tokens de sessão de onboarding válidos:
22
+ -- qualquer leitura dela (um dump, um log de query, um SELECT de suporte)
23
+ -- entregaria a chave que autoriza a conclusão do fluxo de um escritório. O
24
+ -- hash responde "é este?" sem responder "qual é".
25
+ --
26
+ -- ── Por que o índice é GLOBAL ───────────────────────────────────────────────
27
+ --
28
+ -- A conclusão do fluxo roda no contexto do tenant autenticado, e o `state` é
29
+ -- procurado dentro dele — um state de outro escritório simplesmente não é
30
+ -- encontrado. O índice único global existe para a outra direção: garantir que o
31
+ -- mesmo hash não possa ser emitido duas vezes, nem por tenants distintos, o que
32
+ -- tornaria "consumido uma vez" uma afirmação sobre uma linha e não sobre o
33
+ -- valor.
34
+
35
+ CREATE TABLE IF NOT EXISTS channel_onboarding_session (
36
+ id uuid PRIMARY KEY,
37
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
38
+ -- Produto e marca fazem parte da identidade do vínculo que este fluxo vai
39
+ -- criar (ver 0041). Guardá-los aqui é o que permite concluir o fluxo sem
40
+ -- reperguntá-los a quem chama — e portanto sem aceitar que a requisição de
41
+ -- conclusão os escolha.
42
+ product text NOT NULL,
43
+ brand text NOT NULL,
44
+ channel text NOT NULL CHECK (channel IN ('whatsapp')),
45
+ provider text NOT NULL CHECK (provider IN ('meta_whatsapp')),
46
+ -- sha256 hex do state. NUNCA o state.
47
+ state_hash text NOT NULL CHECK (state_hash ~ '^[0-9a-f]{64}$'),
48
+ -- `pending` → `consumed` (sucesso) | `failed` (troca recusada) | `expired`.
49
+ -- `failed` é terminal de propósito: um `code` cuja troca falhou não deve ser
50
+ -- retentado com o mesmo state, porque a falha pode ter sido a Meta já o ter
51
+ -- consumido — e a segunda tentativa não distingue "erro nosso" de "alguém
52
+ -- chegou antes".
53
+ status text NOT NULL DEFAULT 'pending'
54
+ CHECK (status IN ('pending', 'consumed', 'failed', 'expired')),
55
+ -- Quem abriu o fluxo. Humano, sempre: não existe caminho de máquina para
56
+ -- iniciar onboarding de canal.
57
+ created_by uuid REFERENCES "user"(id),
58
+ expires_at timestamptz NOT NULL,
59
+ consumed_at timestamptz,
60
+ -- Classe do erro, nunca a mensagem do provider. A mensagem da Meta pode
61
+ -- carregar identificadores do negócio do cliente.
62
+ failure_code text,
63
+ -- Identificadores devolvidos pelo fluxo, gravados DEPOIS do sucesso. Não são
64
+ -- segredo — a Meta os manda em texto claro em todo webhook.
65
+ waba_ref text,
66
+ phone_ref text,
67
+ created_at timestamptz NOT NULL DEFAULT now(),
68
+ updated_at timestamptz NOT NULL DEFAULT now(),
69
+ CONSTRAINT channel_onboarding_session_tenant_id_uq UNIQUE (tenant_id, id)
70
+ );
71
+
72
+ -- Global. Ver o bloco de comentário acima.
73
+ CREATE UNIQUE INDEX IF NOT EXISTS channel_onboarding_session_state_uq
74
+ ON channel_onboarding_session (state_hash);
75
+
76
+ -- Listagem do que está aberto, por tenant — é o que a tela consulta.
77
+ CREATE INDEX IF NOT EXISTS channel_onboarding_session_pending_idx
78
+ ON channel_onboarding_session (tenant_id, channel, created_at DESC)
79
+ WHERE status = 'pending';
80
+
81
+ ALTER TABLE channel_onboarding_session ENABLE ROW LEVEL SECURITY;
82
+ ALTER TABLE channel_onboarding_session FORCE ROW LEVEL SECURITY;
83
+
84
+ -- FORCE porque a política precisa valer inclusive para o dono da tabela — o
85
+ -- papel que roda migration. É a armadilha da 0035/0036, e ela não se repete
86
+ -- aqui.
87
+ CREATE POLICY channel_onboarding_session_tenant_isolation ON channel_onboarding_session
88
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
89
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
90
+
91
+ COMMENT ON TABLE channel_onboarding_session IS
92
+ 'Sessão de Embedded Signup. Guarda o HASH do state, nunca o state. Consumo é único.';
93
+ COMMENT ON COLUMN channel_onboarding_session.state_hash IS
94
+ 'sha256 hex do state emitido. O valor em claro só existe no navegador do operador.';
95
+ COMMENT ON COLUMN channel_onboarding_session.failure_code IS
96
+ 'Classe do erro. Nunca a mensagem do provider — ela carrega identificadores do negócio do cliente.';