permdock 0.3.1 → 0.5.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 (552) hide show
  1. package/README.md +37 -35
  2. package/dist/{openapi-schema--ARjyfQr.js → 2020-C9VN-x8c.js} +5 -132
  3. package/dist/a2a/index.d.ts +8 -6
  4. package/dist/a2a/index.js +21 -31
  5. package/dist/{adapter-context-mlWsVGGg.js → adapter-context-CRhqyzCk.js} +1 -1
  6. package/dist/adoption-scan-B4R5fnMU.js +107 -0
  7. package/dist/agent/index.d.ts +3 -0
  8. package/dist/agent/index.js +1 -0
  9. package/dist/ai-sdk/index.d.ts +18 -23
  10. package/dist/ai-sdk/index.js +38 -21
  11. package/dist/app-data-MuyzVqX8.js +60 -0
  12. package/dist/approval-header-CJACtHkv.js +23 -0
  13. package/dist/approval-prompt-Dg5LeAcT.js +62 -0
  14. package/dist/approval-prompt-Djo_PyZc.d.ts +57 -0
  15. package/dist/approvals/index.d.ts +5 -3
  16. package/dist/approvals/index.js +8 -5
  17. package/dist/approvals-DJ6Yd5z1.js +995 -0
  18. package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
  19. package/dist/arazzo-B6lebmiQ.js +36 -0
  20. package/dist/{arazzo-BAgw9ap6.js → arazzo-BWhNF-k0.js} +24 -17
  21. package/dist/args-IIolxxKA.js +197 -0
  22. package/dist/astro/index.d.ts +43 -0
  23. package/dist/astro/index.js +27 -0
  24. package/dist/audience-Dg8XsRaO.js +55 -0
  25. package/dist/auth-DRLEizbL.js +178 -0
  26. package/dist/authzen/index.d.ts +26 -6
  27. package/dist/authzen/index.js +228 -75
  28. package/dist/automation-BMZPIVUM.d.ts +114 -0
  29. package/dist/automation-DVjOckjV.js +157 -0
  30. package/dist/better-auth/index.d.ts +8 -6
  31. package/dist/better-auth/index.js +11 -23
  32. package/dist/better-supabase/index.d.ts +438 -86
  33. package/dist/better-supabase/index.js +1027 -184
  34. package/dist/bind--9EFVYWF.d.ts +38 -0
  35. package/dist/{bind-CuzGytRf.js → bind-CZzhx-Vj.js} +22 -10
  36. package/dist/body-CH5of8Ne.js +10 -0
  37. package/dist/body-text-QcXok_SI.js +52 -0
  38. package/dist/build-instance-BMnAudHn.js +52 -0
  39. package/dist/{canonical-json-DemiK4Vp.js → canonical-json-BC8_nMTw.js} +6 -6
  40. package/dist/{capability-2kYTMWDv.d.ts → capability-BSslw1QV.d.ts} +44 -25
  41. package/dist/{capability-4qPdO9gk.js → capability-N1K9IpqS.js} +69 -42
  42. package/dist/casbin-Cr7RLtA3.js +30 -0
  43. package/dist/casbin-Dyu2gnUD.js +234 -0
  44. package/dist/casl-64gpT-8x.js +24 -0
  45. package/dist/casl-DjEDhTCP.js +299 -0
  46. package/dist/catalog/index.d.ts +74 -2
  47. package/dist/catalog/index.js +51 -2
  48. package/dist/catalog-BECMPrwc.js +100 -0
  49. package/dist/catalog-DjhMYYCq.js +1 -0
  50. package/dist/catalog-R_A__RsH.js +52 -0
  51. package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-C9wjsfNY.js} +54 -25
  52. package/dist/catalog-fingerprint-CCP0-yQS.js +153 -0
  53. package/dist/chat/index.d.ts +2 -0
  54. package/dist/chat/index.js +224 -0
  55. package/dist/chat-sdk/index.d.ts +87 -0
  56. package/dist/chat-sdk/index.js +197 -0
  57. package/dist/claims-map-7oQ3ue4X.js +13 -0
  58. package/dist/claims-map-Ca-QX8C6.d.ts +32 -0
  59. package/dist/claude-agent/index.d.ts +7 -14
  60. package/dist/claude-agent/index.js +26 -30
  61. package/dist/clerk/index.d.ts +9 -8
  62. package/dist/clerk/index.js +15 -45
  63. package/dist/cli/bin.js +36 -17
  64. package/dist/cli/index.d.ts +20 -6
  65. package/dist/cli/index.js +164 -4
  66. package/dist/cloud/index.d.ts +102 -63
  67. package/dist/cloud/index.js +240 -347
  68. package/dist/cloud-CIoHNevm.js +87 -0
  69. package/dist/codemod-Bmj9dWMH.js +37 -0
  70. package/dist/codemod-Cxvvc70T.js +2084 -0
  71. package/dist/{collect-DSTE3ml9.js → collect-CrZJJnEF.js} +79 -17
  72. package/dist/collect-DloHbDVa.js +55 -0
  73. package/dist/collect-FlXJlhus.js +1 -0
  74. package/dist/command-L8EstFNm.js +6 -0
  75. package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
  76. package/dist/compile/index.d.ts +1 -1
  77. package/dist/compile/index.js +1 -2
  78. package/dist/{compile-Ba4blROR.js → compile-CMqMx_IN.js} +13 -9
  79. package/dist/{compile-BkRyBDBw.d.ts → compile-Ckm2HqBZ.d.ts} +5 -3
  80. package/dist/completion-BqhRBJD8.js +143 -0
  81. package/dist/config-B46rm9sc.js +26 -0
  82. package/dist/{config-BJiM-xAm.js → config-bs_kZeYq.js} +15 -10
  83. package/dist/{config-report-CdCgbRnV.js → config-report-DUgj62Ai.js} +17 -8
  84. package/dist/{connection-CU8MQxRi.js → connection-DjhIJSs2.js} +8 -6
  85. package/dist/constants-DcJABbLW.js +20 -0
  86. package/dist/{context-Bcy85rGi.js → context-bbFHR-0D.js} +16 -5
  87. package/dist/convex/index.d.ts +11 -10
  88. package/dist/convex/index.js +18 -18
  89. package/dist/create-0VZ7Pdiu.d.ts +107 -0
  90. package/dist/create-BDS7wc3y.d.ts +138 -0
  91. package/dist/create-BFfm17UA.d.ts +110 -0
  92. package/dist/create-BWRSXgOF.js +403 -0
  93. package/dist/create-C3b4B5eF.js +130 -0
  94. package/dist/create-DnrmLnLW.js +323 -0
  95. package/dist/{credential-8P81ff4u.js → credential-DM2um3ha.js} +153 -32
  96. package/dist/credentials-DPyHmYz5.js +74 -0
  97. package/dist/deciding-columns-Sq12mEA6.js +123 -0
  98. package/dist/delegation-DaXVdDhM.js +286 -0
  99. package/dist/delegation-input-FTxMK3ji.js +19 -0
  100. package/dist/delivery/index.d.ts +5 -0
  101. package/dist/delivery/index.js +5 -0
  102. package/dist/delivery-DCAgNyCn.js +499 -0
  103. package/dist/delivery-event-xX4obB8F.js +40 -0
  104. package/dist/denied-text-D8jDHgsl.d.ts +19 -0
  105. package/dist/{describe-DMYiCjJV.d.ts → describe-BZUsixiL.d.ts} +11 -2
  106. package/dist/{describe-B_gVZEVi.js → describe-BybMRY7A.js} +10 -5
  107. package/dist/device-BARQ8eYG.js +193 -0
  108. package/dist/diff-CxZo9pTq.js +37 -0
  109. package/dist/{diff-2VaeChzX.js → diff-DCePiN6M.js} +61 -55
  110. package/dist/doctor-CxiwSfqH.js +44 -0
  111. package/dist/doctor-DFjUxf2f.js +2361 -0
  112. package/dist/drizzle/index.d.ts +5 -3
  113. package/dist/drizzle/index.js +6 -14
  114. package/dist/elysia/index.d.ts +16 -13
  115. package/dist/elysia/index.js +14 -13
  116. package/dist/env-DJZrPjRi.js +8 -0
  117. package/dist/errors-BDUwg2qC.js +27 -0
  118. package/dist/{errors-Cz5zn0mW.js → errors-BKy0EfhN.js} +6 -10
  119. package/dist/errors-BWXBMaKK.d.ts +11 -0
  120. package/dist/{errors-DPdqI50v.d.ts → errors-C2PXIv4c.d.ts} +2 -2
  121. package/dist/errors-CWTY6uih.js +143 -0
  122. package/dist/evaluate-_R8QkWaf.js +1732 -0
  123. package/dist/evaluations-16UqqQ6W.d.ts +32 -0
  124. package/dist/{evaluations-tz6D9A2G.js → evaluations-DZ2CbV5k.js} +181 -32
  125. package/dist/eve/index.d.ts +12 -16
  126. package/dist/eve/index.js +23 -29
  127. package/dist/event-time-CsegxXxB.js +23 -0
  128. package/dist/expiry-D4_YKRKD.js +52 -0
  129. package/dist/express/index.d.ts +13 -10
  130. package/dist/express/index.js +14 -12
  131. package/dist/fastify/index.d.ts +26 -13
  132. package/dist/fastify/index.js +17 -36
  133. package/dist/fetch-adapter-g0ypLkID.js +120 -0
  134. package/dist/fetch-adapter-qKJBuRqy.d.ts +31 -0
  135. package/dist/fields-D9tPVUUS.js +203 -0
  136. package/dist/files-C5ZI27eV.js +124 -0
  137. package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
  138. package/dist/{fixtures-ht28Cplr.js → fixtures-B7XrLeY7.js} +12 -12
  139. package/dist/flags-C3DktBDY.js +86 -0
  140. package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-D2lZt4Dd.js} +78 -65
  141. package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
  142. package/dist/grantee-Cn1HyUsI.js +254 -0
  143. package/dist/grantee-conditions-CcXf8hMM.js +271 -0
  144. package/dist/grantee-match-DHArqY82.js +142 -0
  145. package/dist/h3/index.d.ts +37 -0
  146. package/dist/h3/index.js +35 -0
  147. package/dist/headers-BIjfMaoC.js +8 -0
  148. package/dist/headers-CIbDTUTK.d.ts +7 -0
  149. package/dist/{helpers-H_KARDVC.js → helpers-DTcZOzhF.js} +47 -39
  150. package/dist/hono/index.d.ts +12 -9
  151. package/dist/hono/index.js +14 -13
  152. package/dist/{hosted-DJQw46hn.js → hosted-BZIAYvon.js} +64 -15
  153. package/dist/hosted-DBDX0M-P.d.ts +78 -0
  154. package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
  155. package/dist/{http-DPuDaChb.d.ts → http-B_LphE5Y.d.ts} +1 -2
  156. package/dist/http-DXJ37RDA.js +46 -0
  157. package/dist/{http-BIhmLfCc.js → http-ujKm0JXE.js} +3 -26
  158. package/dist/{openapi-import-cmwetbzw.js → import-CNV7I2Dw.js} +30 -26
  159. package/dist/{rls-import-4lLW_pbb.js → import-DMmOG27O.js} +271 -128
  160. package/dist/index-BcHcPyIo.d.ts +110 -0
  161. package/dist/index-CaxrMD1A.d.ts +1 -0
  162. package/dist/index-Chrs5fQ-.d.ts +210 -0
  163. package/dist/index-CxGUswIC.d.ts +431 -0
  164. package/dist/index-D8EovfsP.d.ts +91 -0
  165. package/dist/index-DOVf7RJv.d.ts +900 -0
  166. package/dist/{index-C8z4RjQV.d.ts → index-SGdIQyzc.d.ts} +34 -16
  167. package/dist/index.d.ts +20 -14
  168. package/dist/index.js +33 -23
  169. package/dist/init-DeMYsnYJ.js +96 -0
  170. package/dist/init-GG6Jh8HD.js +95 -0
  171. package/dist/{instance-options-jARdwfPN.d.ts → instance-options-Omfd793B.d.ts} +2 -2
  172. package/dist/instance-snapshot-Cxq4ygFc.js +600 -0
  173. package/dist/instant-BluqDvyS.js +19 -0
  174. package/dist/internals-Dk8A8fX3.js +17 -0
  175. package/dist/{json-schema-BsAflUGW.js → json-schema-B-vAwlde.js} +46 -9
  176. package/dist/json-schema-CN_iLFH2.d.ts +40 -0
  177. package/dist/jwt/index.d.ts +18 -11
  178. package/dist/jwt/index.js +166 -145
  179. package/dist/{kernel-_RRHmnMY.js → kernel-5JatXyND.js} +120 -43
  180. package/dist/kysely/index.d.ts +5 -3
  181. package/dist/kysely/index.js +6 -14
  182. package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
  183. package/dist/load-BxEtwlBS.js +1 -0
  184. package/dist/{load-DhWEB9P1.js → load-DFS_ry0W.js} +31 -7
  185. package/dist/{load-jose-BL6LH4bL.js → load-jose-DZ4dnjri.js} +1 -4
  186. package/dist/login-Dfe5RlYk.js +68 -0
  187. package/dist/login-imhuwvn3.js +254 -0
  188. package/dist/manifest-C8IzW3Do.js +42 -0
  189. package/dist/manifest-CtKR3GXL.js +370 -0
  190. package/dist/manifest-json-CAGzls-C.js +9 -0
  191. package/dist/{manifest-uDS9y5de.d.ts → manifest-mx5ghSFM.d.ts} +9 -2
  192. package/dist/manifest-schema-C0kK_8V0.js +399 -0
  193. package/dist/{map-claims-Z4QZOiFF.js → map-claims-D3oRmTk0.js} +19 -25
  194. package/dist/{map-error-pgUWNNtG.js → map-error-TCAdw-eW.js} +5 -5
  195. package/dist/{markers-B0hYLvMD.js → markers-C6joDI3o.js} +8 -3
  196. package/dist/mcp/index.d.ts +36 -9
  197. package/dist/mcp/index.js +476 -448
  198. package/dist/{rls-migrate-CN4TlJTO.js → migrate-BShP7Vf4.js} +119 -20
  199. package/dist/nest/index.d.ts +12 -9
  200. package/dist/nest/index.js +20 -20
  201. package/dist/next/index.d.ts +40 -20
  202. package/dist/next/index.js +37 -39
  203. package/dist/next/plugin.d.ts +1 -1
  204. package/dist/next/plugin.js +2 -2
  205. package/dist/node/index.d.ts +10 -10
  206. package/dist/node/index.js +7 -9
  207. package/dist/ocsf-BFp-PZZA.js +126 -0
  208. package/dist/openai/index.d.ts +9 -16
  209. package/dist/openai/index.js +18 -25
  210. package/dist/openapi/index.d.ts +3 -3
  211. package/dist/openapi/index.js +1 -1
  212. package/dist/{openapi-BqRBvAng.js → openapi-B7HZ0f-r.js} +9 -12
  213. package/dist/{openapi-CqqEbHsL.js → openapi-DTaEmTSW.js} +23 -26
  214. package/dist/openapi-ahr8rHu3.js +138 -0
  215. package/dist/operation-findings-DgATDpvh.js +36 -0
  216. package/dist/operations-CxybZErY.d.ts +59 -0
  217. package/dist/orpc/index.d.ts +13 -10
  218. package/dist/orpc/index.js +23 -20
  219. package/dist/otel/index.d.ts +3 -2
  220. package/dist/otel/index.js +53 -27
  221. package/dist/output-Dyw8fyPF.js +88 -0
  222. package/dist/output-files-B3IZJksC.js +1 -0
  223. package/dist/output-files-DhRBBE4n.js +138 -0
  224. package/dist/{rls-fields-CZOMlwHl.js → ownership-BatFgAA4.js} +2393 -2349
  225. package/dist/{package-root-zGkxO3oN.js → package-root-Dk_ETdkp.js} +4 -3
  226. package/dist/{parse-BuyjxhZV.js → parse-Cxq0WxrA.js} +3 -3
  227. package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-CVSs6J9I.js} +2 -4
  228. package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DgqtruPn.d.ts} +1 -1
  229. package/dist/parse-request-D_tUXG6M.js +26 -0
  230. package/dist/paths-DXSHAMIa.js +42 -0
  231. package/dist/pdp/index.d.ts +7 -3
  232. package/dist/pdp/index.js +47 -29
  233. package/dist/{peer-BOr_kJE1.js → peer-C8swJ2GH.js} +23 -4
  234. package/dist/peer-CRvC7Hg1.js +1 -0
  235. package/dist/permdock-B9JxkBwC.js +868 -0
  236. package/dist/permdock-D7eeLJER.d.ts +502 -0
  237. package/dist/permission-registry-Br4jSNVi.js +385 -0
  238. package/dist/permissions-B-wCP5ZC.js +30 -0
  239. package/dist/{permissions-RJrovw98.js → permissions-CZKiUfFM.js} +4 -3
  240. package/dist/{permissions-CDgvKG5L.d.ts → permissions-oEWfl-kl.d.ts} +1 -2
  241. package/dist/policies-CO1oOymN.js +295 -0
  242. package/dist/{policy-D2W7uv19.d.ts → policy-Czo4n3Hs.d.ts} +2 -2
  243. package/dist/policy-DLZJ5gAp.js +399 -0
  244. package/dist/policy-from-catalog-DzhEc3Y9.js +450 -0
  245. package/dist/policy-module-CTlwMvLI.js +89 -0
  246. package/dist/{policy-IB6yxpC3.d.ts → policy-types-DzwivBgR.d.ts} +939 -1057
  247. package/dist/postgrest-CW0IqC-U.js +566 -0
  248. package/dist/postgrest-NUqZT-RB.d.ts +100 -0
  249. package/dist/powersync-BHfuehf3.js +62 -0
  250. package/dist/powersync-CbzkqL_M.js +1 -0
  251. package/dist/{powersync-Dv1_aWF7.js → powersync-bYUDLOAO.js} +30 -32
  252. package/dist/preset/index.d.ts +146 -0
  253. package/dist/preset/index.js +1 -0
  254. package/dist/preset-DZdU0tne.js +1768 -0
  255. package/dist/principal-source-pHneukrx.js +103 -0
  256. package/dist/prisma/index.d.ts +6 -3
  257. package/dist/prisma/index.js +6 -12
  258. package/dist/{problem-B43ztKIm.js → problem-CTviyH5L.js} +18 -27
  259. package/dist/{problem-details-DX075l9V.d.ts → problem-details-DXlt-vks.d.ts} +1 -1
  260. package/dist/{problem-details-_AMOYz-w.js → problem-details-cn5X30gZ.js} +45 -42
  261. package/dist/problem-types-CJ2325SZ.js +5 -0
  262. package/dist/{prompt-CXYhLfsS.js → prompt-DfZLJLIn.js} +2 -2
  263. package/dist/protected-DEPHc2xV.d.ts +38 -0
  264. package/dist/{protected-utU-SLuy.js → protected-DfjY5mLA.js} +37 -8
  265. package/dist/{provider-CSQd7m92.js → provider-CC-u3bYP.js} +23 -10
  266. package/dist/{provider-DZY281jO.d.ts → provider-DMVt6yYn.d.ts} +1 -1
  267. package/dist/{provider-store-Cfbvo4DE.js → provider-store-B5Yrkhzr.js} +6 -3
  268. package/dist/pull-BgIfno6w.js +256 -0
  269. package/dist/pull-ByOks_Tg.js +1 -0
  270. package/dist/push-CmFVBOeI.js +116 -0
  271. package/dist/pushed-policy-BImMTTIM.d.ts +57 -0
  272. package/dist/pushed-policy-DobP7AHI.js +238 -0
  273. package/dist/react/index.d.ts +8 -8
  274. package/dist/react/index.js +6 -6
  275. package/dist/react/provider-client.d.ts +1 -1
  276. package/dist/react/provider-client.js +1 -2
  277. package/dist/react-native/index.d.ts +24 -63
  278. package/dist/react-native/index.js +45 -27
  279. package/dist/{refs-KYb4ntaz.js → refs-K6eWUJ0H.js} +1 -1
  280. package/dist/relation-walk-3OyDBF0S.js +186 -0
  281. package/dist/relations-BnCWnJGI.js +163 -0
  282. package/dist/renames-CGj7YwcQ.js +175 -0
  283. package/dist/{replay-CYyiLiwO.d.ts → replay-D7iKcLFe.d.ts} +1 -1
  284. package/dist/{replay-BsOyTrdV.js → replay-Du1tJjG7.js} +2 -1
  285. package/dist/request-context-BTRTG7OT.js +71 -0
  286. package/dist/request-context-C2_1lchh.d.ts +5 -0
  287. package/dist/request-schema-CMreVuFM.js +148 -0
  288. package/dist/resend/index.d.ts +126 -0
  289. package/dist/resend/index.js +379 -0
  290. package/dist/resource-relations-D_jw0BFx.js +243 -0
  291. package/dist/retire-CALjIQ7e.js +240 -0
  292. package/dist/{revocations-Cy06JNx2.d.ts → revocations-R07lFBnX.d.ts} +2 -0
  293. package/dist/rls-BA8an5Ap.js +336 -0
  294. package/dist/{rls-DiinlRML.js → rls-j6t43t7n.js} +1066 -1068
  295. package/dist/role-builder-CXbis5-X.js +623 -0
  296. package/dist/{rpc-DL9Sqvb1.js → rpc-aPyNi6tA.js} +18 -8
  297. package/dist/run-CNsApDpd.js +1 -0
  298. package/dist/run-DOfMtc9V.js +723 -0
  299. package/dist/run-ticket-BjhTOi6u.d.ts +124 -0
  300. package/dist/run-ticket-sxFI6LAk.js +337 -0
  301. package/dist/{runtime-CB8gQFvS.js → runtime-D3VTAiCA.js} +1 -1
  302. package/dist/{saas-DhpJjnBA.js → saas-FO-Q8oj0.js} +18 -15
  303. package/dist/scan-BOI5tJ-6.js +29 -0
  304. package/dist/{schema-D3MnDMxa.js → schema-B4v0Qrac.js} +106 -10
  305. package/dist/schema-yLuBWWsh.js +130 -0
  306. package/dist/scim/index.d.ts +2 -2
  307. package/dist/scim/index.js +1 -2
  308. package/dist/{scim-BFGBBAqR.js → scim-ClHtpyV3.js} +213 -87
  309. package/dist/{scopes-CVdPrWN-.js → scopes-CUPSjo9q.js} +20 -7
  310. package/dist/server/index.d.ts +13 -134
  311. package/dist/server/index.js +13 -10
  312. package/dist/server-DqCT4CgZ.js +453 -0
  313. package/dist/{create-B1d519XK.d.ts → server-types-BLd4MnBX.d.ts} +89 -70
  314. package/dist/{sha256-BO0swRXL.js → sha256-D9ihqmvd.js} +8 -1
  315. package/dist/shared-CD11nBbC.js +304 -0
  316. package/dist/{shared-DRu8VFVM.js → shared-DvvFzYXe.js} +8 -9
  317. package/dist/signals-D3NcTKUS.js +32 -0
  318. package/dist/sink-BEZSlMS_.js +957 -0
  319. package/dist/sink-Bj_lDUDq.d.ts +225 -0
  320. package/dist/{skills-Biq5uup3.js → skills-B6lVdD_W.js} +16 -8
  321. package/dist/skills-CTYwkl44.js +51 -0
  322. package/dist/snapshot-CXUWfsNy.js +269 -0
  323. package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-DZHQ0KF6.js} +11 -5
  324. package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-DqK9eDmM.d.ts} +7 -5
  325. package/dist/solid/index.d.ts +24 -25
  326. package/dist/solid/index.js +41 -11
  327. package/dist/solid-start/index.d.ts +52 -0
  328. package/dist/solid-start/index.js +49 -0
  329. package/dist/{sources-BdOYLw9j.d.ts → sources-BV9j0DeZ.d.ts} +11 -13
  330. package/dist/{sources-4p0-4xQm.js → sources-vs5dGOsS.js} +147 -142
  331. package/dist/sql-B5T1IL83.js +55 -0
  332. package/dist/src-DLfUWFAI.js +1851 -0
  333. package/dist/ssf/index.d.ts +4 -4
  334. package/dist/ssf/index.js +440 -106
  335. package/dist/standard-webhooks-6lg8v_is.d.ts +88 -0
  336. package/dist/standard-webhooks-DgFFD-Ga.js +228 -0
  337. package/dist/storage-C2bWdvJH.js +132 -0
  338. package/dist/{store-DMAWwAJV.js → store-4u_hxjWa.js} +42 -55
  339. package/dist/{store-options-ByhZq1jV.js → store-options-BqEakYRa.js} +38 -24
  340. package/dist/{stores-BT2pSCub.js → stores-BbsD4fAR.js} +7 -3
  341. package/dist/stores-ClZUgeoP.d.ts +30 -0
  342. package/dist/{stream-D1RXubw2.d.ts → stream-7-xmnf3U.d.ts} +2 -2
  343. package/dist/{stream-DDpvynSB.js → stream-CvdUS9V1.js} +10 -2
  344. package/dist/style-DiKWnAvn.js +1 -0
  345. package/dist/{style-CTm1AXDT.js → style-Uao76wO7.js} +3 -2
  346. package/dist/subject-BGoM5o8B.js +17 -0
  347. package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
  348. package/dist/supabase/index.d.ts +23 -106
  349. package/dist/supabase/index.js +5 -4
  350. package/dist/supabase/middleware.d.ts +13 -6
  351. package/dist/supabase/middleware.js +16 -11
  352. package/dist/supabase-9pqI57pW.js +92 -0
  353. package/dist/{supabase-C0qESOa-.js → supabase-Dxlp8A94.js} +124 -247
  354. package/dist/supabase-hook-CXcGpHqq.js +1 -0
  355. package/dist/{supabase-hook-ntETYCQu.js → supabase-hook-DdDQhMlw.js} +1659 -819
  356. package/dist/{supautils-u8jXTGrt.js → supautils-B6HYL9eL.js} +2 -2
  357. package/dist/support-session-C6xJQYut.js +158 -0
  358. package/dist/svelte/Protected.svelte +11 -4
  359. package/dist/svelte/index.d.ts +7 -7
  360. package/dist/svelte/index.js +26 -12
  361. package/dist/svelte/runtime.d.ts +10 -3
  362. package/dist/svelte/runtime.js +4 -3
  363. package/dist/svelte/source.d.ts +7 -7
  364. package/dist/svelte/source.js +5 -5
  365. package/dist/sveltekit/index.d.ts +51 -0
  366. package/dist/sveltekit/index.js +27 -0
  367. package/dist/tanstack-start/index.d.ts +51 -0
  368. package/dist/tanstack-start/index.js +40 -0
  369. package/dist/target-Bw37TFNA.js +51 -0
  370. package/dist/{validity-uOp8UpYn.js → tenancy-DN2QAx9z.js} +6 -42
  371. package/dist/terminal/index.d.ts +11 -9
  372. package/dist/terminal/index.js +36 -316
  373. package/dist/testing/index.d.ts +391 -106
  374. package/dist/testing/index.js +4574 -1286
  375. package/dist/testing/saas/index.d.ts +9 -8
  376. package/dist/testing/saas/index.js +2 -2
  377. package/dist/testing/saas/permissions.d.ts +1 -1
  378. package/dist/testing/saas/permissions.js +1 -2
  379. package/dist/text-BMFRfBqc.js +8 -0
  380. package/dist/{text-diff-BdkkYSbi.js → text-diff-BQcUapOb.js} +1 -1
  381. package/dist/thenable-yD7SabqG.js +31 -0
  382. package/dist/time-DkWjdL98.js +71 -0
  383. package/dist/{token-CIWxJOCh.js → token-BLZcjQgt.js} +15 -6
  384. package/dist/tool-refusal-CMLN5xhn.js +41 -0
  385. package/dist/trpc/index.d.ts +12 -9
  386. package/dist/trpc/index.js +18 -17
  387. package/dist/{types-DbjRN55U.d.ts → types-5Ut_pbKy.d.ts} +141 -13
  388. package/dist/types-BNORmDM8.d.ts +172 -0
  389. package/dist/types-BjV-fqcA.d.ts +57 -0
  390. package/dist/{types-QCuYJh4q.d.ts → types-BqAKcRNW.d.ts} +2 -1
  391. package/dist/types-C8-KNsFf.d.ts +107 -0
  392. package/dist/{types-aqOHTHcp.d.ts → types-CCpc2Dcv.d.ts} +77 -17
  393. package/dist/{types-Dnemwd77.d.ts → types-CZTnPFzM.d.ts} +50 -11
  394. package/dist/{types-C97IskBw.d.ts → types-CdlxsYQh.d.ts} +23 -11
  395. package/dist/{types-DxiCGmmd.d.ts → types-ClZaWzfb.d.ts} +9 -6
  396. package/dist/types-DX0LaKTb.d.ts +157 -0
  397. package/dist/types-DiQjupG2.d.ts +332 -0
  398. package/dist/types-DqzsnlSA.js +67 -0
  399. package/dist/types-_sHy8BQf.d.ts +65 -0
  400. package/dist/types-fbZ7JEXr.d.ts +89 -0
  401. package/dist/types-tIgIeIKJ.d.ts +66 -0
  402. package/dist/unplugin/index.d.ts +1 -1
  403. package/dist/unplugin/index.js +2 -2
  404. package/dist/usage-CHOtwL19.js +1 -0
  405. package/dist/usage-DHtE8j4E.js +32 -0
  406. package/dist/{usage-ItS7nLcl.js → usage-T5mXq98v.js} +56 -13
  407. package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-Bg5Tpluk.d.ts} +1 -1
  408. package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
  409. package/dist/validity-CZ7-y0Cj.js +79 -0
  410. package/dist/{verifier-BxBo6I44.js → verifier-DHuh-okk.js} +68 -21
  411. package/dist/verify-DbCL9bnB.js +1770 -0
  412. package/dist/{version-BvacVTZR.js → version-CEbQ5UeM.js} +4 -3
  413. package/dist/version-DXkYUISx.js +1 -0
  414. package/dist/{views-Soe7bGSC.js → views-DTIIO-oQ.js} +41 -2
  415. package/dist/{permissions-DttnqqxC.d.ts → vocabulary-D6lPdb4Z.d.ts} +120 -29
  416. package/dist/vue/index.d.ts +25 -21
  417. package/dist/vue/index.js +25 -9
  418. package/dist/waiter-BGUtGYoU.js +67 -0
  419. package/dist/waiter-CKy4MQSY.d.ts +36 -0
  420. package/dist/watch-Cb-NcaUw.js +1 -0
  421. package/dist/{watch-DVBJ9cBq.js → watch-CszoTJMq.js} +47 -8
  422. package/dist/webmcp/index.d.ts +2 -2
  423. package/dist/webmcp/index.js +13 -37
  424. package/dist/where-scope-Dv-WTP-w.js +306 -0
  425. package/dist/who-can-D0QoYbEb.js +364 -0
  426. package/dist/wire-delegation-Cv3W2eaM.js +36 -0
  427. package/dist/wire-denial-C7PaVYEM.js +27 -0
  428. package/dist/workflow/index.d.ts +5 -0
  429. package/dist/workflow/index.js +223 -0
  430. package/dist/workflow-sdk/index.d.ts +56 -0
  431. package/dist/workflow-sdk/index.js +78 -0
  432. package/dist/wrap-Bd1Tejur.d.ts +15 -0
  433. package/dist/wrap-gX61EtpB.js +27 -0
  434. package/package.json +140 -41
  435. package/schemas/access-event-v1.json +118 -0
  436. package/schemas/approval-request-v1.json +34 -1
  437. package/schemas/approval-signature-v1.json +94 -0
  438. package/schemas/automation-event-v1.json +113 -0
  439. package/schemas/capability-v1.json +84 -0
  440. package/schemas/catalog-event-v1.json +87 -0
  441. package/schemas/catalog-v1.json +129 -12
  442. package/schemas/cli-commands-v1.json +164 -0
  443. package/schemas/cli-result-v1.json +96 -0
  444. package/schemas/cloud-api-v1.json +5407 -0
  445. package/schemas/connector-v1.json +408 -0
  446. package/schemas/credential-event-v1.json +81 -0
  447. package/schemas/credential-v1.json +134 -0
  448. package/schemas/decision-event-v1.json +288 -0
  449. package/schemas/delivery-event-v1.json +117 -0
  450. package/schemas/directory-event-v1.json +85 -0
  451. package/schemas/evidence-export-v1.json +54 -0
  452. package/schemas/membership-event-v1.json +93 -0
  453. package/schemas/policy-v1.json +68 -0
  454. package/schemas/pushed-policy-v1.json +236 -0
  455. package/schemas/run-v1.json +162 -0
  456. package/schemas/snapshot-v1.json +138 -0
  457. package/schemas/supabase-claims-v1.json +4 -4
  458. package/schemas/supabase-manifest-v1.json +5 -1
  459. package/schemas/support-session-v1.json +111 -0
  460. package/skills/permdock/SKILL.md +16 -12
  461. package/skills/permdock-agents/SKILL.md +40 -23
  462. package/skills/permdock-agents/references/operations-automations-chat.md +221 -0
  463. package/skills/permdock-agents/references/runtimes.md +22 -18
  464. package/skills/permdock-approvals/SKILL.md +29 -13
  465. package/skills/permdock-approvals/references/delivery-and-waiters.md +172 -0
  466. package/skills/permdock-approvals/references/stores-and-resume.md +21 -7
  467. package/skills/permdock-audit/SKILL.md +3 -3
  468. package/skills/permdock-credentials/SKILL.md +5 -5
  469. package/skills/permdock-credentials/references/tokens-keys-links.md +19 -10
  470. package/skills/permdock-data/SKILL.md +6 -6
  471. package/skills/permdock-data/references/queries-and-rls.md +13 -8
  472. package/skills/permdock-data/references/relationships.md +3 -3
  473. package/skills/permdock-tenancy/SKILL.md +20 -7
  474. package/skills/permdock-tenancy/references/contacts-and-support-sessions.md +122 -0
  475. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +6 -4
  476. package/skills/permdock-wire/SKILL.md +18 -12
  477. package/skills/permdock-wire/references/adapters.md +146 -50
  478. package/skills/permdock-wire/references/policy-options.md +15 -6
  479. package/skills/permdock-wire/references/presets-and-recipes.md +58 -0
  480. package/dist/approvals-DbYAuNkG.js +0 -224
  481. package/dist/arazzo-C9chp3ST.js +0 -54
  482. package/dist/bind-C_ob3LKa.d.ts +0 -25
  483. package/dist/catalog-CRW5Mx_B.js +0 -2
  484. package/dist/catalog-CYj-mX2O.js +0 -48
  485. package/dist/catalog-DWF5cYqB.js +0 -55
  486. package/dist/cloud-CfwZ39Rl.js +0 -50
  487. package/dist/cloud-DWLXO2af.js +0 -118
  488. package/dist/collect-CHB22Sx4.js +0 -83
  489. package/dist/collect-DSWOk19R.js +0 -2
  490. package/dist/commands-BeaGNj_o.js +0 -97
  491. package/dist/config-ir40y8m0.js +0 -36
  492. package/dist/create-CMwIl-w2.js +0 -253
  493. package/dist/delegation-D8pUEuce.js +0 -103
  494. package/dist/diff-C9RDjjhs.js +0 -48
  495. package/dist/doctor-Djh6JDEk.js +0 -53
  496. package/dist/doctor-DvH0IQff.js +0 -1194
  497. package/dist/doctor-collect-CjdO0wQX.js +0 -665
  498. package/dist/duration-xK2jGFVc.js +0 -25
  499. package/dist/errors-CWfoNg9N.js +0 -47
  500. package/dist/errors-DRnGZHzF.js +0 -30
  501. package/dist/evaluation-items-CfCCejQd.js +0 -40
  502. package/dist/expiry-BoBJ8Tvc.js +0 -7
  503. package/dist/fields-BMl1RxoX.js +0 -26
  504. package/dist/grantee-Cv5Sogfv.js +0 -602
  505. package/dist/headers-BZSDieNT.js +0 -6
  506. package/dist/headers-Cf-zWvsO.d.ts +0 -6
  507. package/dist/ids-DC4V4CFu.js +0 -18
  508. package/dist/index-0nNqHspG.d.ts +0 -6
  509. package/dist/index-BNc7Wer_.d.ts +0 -483
  510. package/dist/index-C7Tw7MaW.d.ts +0 -201
  511. package/dist/instance-options-Dhhcnm3y.js +0 -22
  512. package/dist/load-DwJUafYN.js +0 -2
  513. package/dist/manifest-iaoBXHKc.js +0 -783
  514. package/dist/map-AcxK9o_D.js +0 -147
  515. package/dist/may-use-DndQ5tQ-.js +0 -77
  516. package/dist/openapi-CQTsgKoB.js +0 -164
  517. package/dist/permdock-CbgvLnuo.js +0 -3479
  518. package/dist/permissions-DVoF6dMt.js +0 -662
  519. package/dist/policy-xyWg7QdR.js +0 -1122
  520. package/dist/powersync-CrBX8hY6.js +0 -2
  521. package/dist/powersync-DE19b9dT.js +0 -62
  522. package/dist/protected-BdnIEFkY.d.ts +0 -27
  523. package/dist/protected-xAr24FEC.d.ts +0 -40
  524. package/dist/relations-cjOL01hZ.js +0 -505
  525. package/dist/rls-P-BeBx0n.js +0 -238
  526. package/dist/rls-graph-CHwx0V8q.js +0 -435
  527. package/dist/rls-ownership-DcYlZ09B.js +0 -843
  528. package/dist/rls-shared-Cewd_-zI.js +0 -495
  529. package/dist/rls-verify-CMv9H3wV.js +0 -746
  530. package/dist/run-BWc9qqvE.js +0 -174
  531. package/dist/server-DlZ28Oz5.js +0 -268
  532. package/dist/sink-B1fZzIMf.d.ts +0 -104
  533. package/dist/sink-Bd9PSyfq.js +0 -141
  534. package/dist/skills-C5CbhhpG.js +0 -56
  535. package/dist/snapshot-Enxd0n-s.js +0 -975
  536. package/dist/sql-xdg5f4Fi.js +0 -27
  537. package/dist/src-BDwEZWrZ.js +0 -568
  538. package/dist/store-jOBYpiXC.d.ts +0 -36
  539. package/dist/stores-C0IMQrGI.d.ts +0 -52
  540. package/dist/subject-C6dLxqHU.js +0 -24
  541. package/dist/supabase-config-Bj3iDH2I.js +0 -472
  542. package/dist/supabase-hook-gd3s8bxB.js +0 -2
  543. package/dist/supabase-s4o9s1O1.js +0 -83
  544. package/dist/thenable-okc2xDsj.js +0 -10
  545. package/dist/types-BnIEz1sU.d.ts +0 -48
  546. package/dist/types-Bvc4oWT6.d.ts +0 -9
  547. package/dist/types-CKNSyIf_.d.ts +0 -52
  548. package/dist/types-Cq_H8N_X.d.ts +0 -197
  549. package/dist/usage-B0YuTQRO.js +0 -42
  550. package/dist/usage-DorAJiU_.js +0 -2
  551. package/dist/vocabulary-DA8a8zif.d.ts +0 -56
  552. package/dist/watch-BwxLTYMZ.js +0 -2
@@ -1,6 +1,6 @@
1
1
  # Adapter factories
2
2
 
3
- Every server adapter exports `createPermDock`. The import path names the framework. Agent runtime adapters (AI SDK, Claude Agent SDK, Eve, OpenAI Agents SDK, MCP, A2A, WebMCP, terminal) are in the `permdock-agents` skill; ORM adapters and Convex in the `permdock-data` skill. React has no factory: it exports hooks and `<Protected>` from `permdock/react`.
3
+ Every server adapter exports `createPermDock`. The import path names the framework. The implementation entries `permdock/resend`, `permdock/chat-sdk` and `permdock/workflow-sdk` export functions named after what they return, not a factory. Agent runtime adapters (AI SDK, Claude Agent SDK, Eve, OpenAI Agents SDK, MCP, A2A, WebMCP, terminal) are in the `permdock-agents` skill; ORM adapters and Convex in the `permdock-data` skill. React has no factory: it exports hooks and `<Protected>` from `permdock/react`.
4
4
 
5
5
  ## Next.js — `permdock/next`
6
6
 
@@ -8,12 +8,13 @@ File: `src/permdock/server.ts`
8
8
 
9
9
  ```ts
10
10
  import { createPermDock } from "permdock/next";
11
+
11
12
  import { policy } from "../policy";
12
13
 
13
14
  export const {
14
15
  getPermDock,
15
16
  getPermission,
16
- requireAccess,
17
+ requirePermission,
17
18
  PermDockProvider,
18
19
  permdockHandler,
19
20
  } = createPermDock(policy, {
@@ -21,7 +22,7 @@ export const {
21
22
  });
22
23
  ```
23
24
 
24
- Needs Next.js 16.3 or later. Guard with `getPermission(permissions.post.update, post)` or `assert`. For a page or Server Action that must stop on a denial, `await requireAccess({ permission, data, tenant })` calls `forbidden()` (or `unauthorized()` for a signed-out user); enable `experimental.authInterrupts` and add `app/forbidden.tsx` and `app/unauthorized.tsx`. When only part of a page is gated, let a Server Component `assert` inside `<PermissionBoundary denied={...} approval={...}>` from `permdock/next/client` (with a `Suspense` inside it); `usePermissionBoundary()` in the fallback gives the permission, the approval `token` and `retry()`. Client components use `permdock/react` inside the server `PermDockProvider`, which never awaits: it streams a `snapshotPromise`, and permission hooks answer `pending` until it lands (add `suspend` to make them suspend).
25
+ Needs Next.js 16.3 or later. Guard with `getPermission(permissions.post.update, post)` or `assert`. For a page or Server Action that must stop on a denial, `await requirePermission(permission, data, { tenant })` calls `forbidden()` (or `unauthorized()` for a signed-out user); enable `experimental.authInterrupts` and add `app/forbidden.tsx` and `app/unauthorized.tsx`. When only part of a page is gated, let a Server Component `assert` inside `<PermissionBoundary denied={...} approval={...}>` from `permdock/next/client` (with a `Suspense` inside it); `usePermissionBoundary()` in the fallback gives the permission, the approval `token` and `retry()`. Client components use `permdock/react` inside the server `PermDockProvider`, which never awaits: it streams a `snapshotPromise`, and permission hooks answer `pending` until it lands (add `suspend` to make them suspend).
25
26
 
26
27
  With `cacheComponents`, when permission UI (nav items, row actions) must be prefetched, the app owns the cache directive and `getSnapshot` fills it. Without the factory, call `snapshotFor` and set `cacheLife(cacheLifeFor(snapshot))` and `cacheTag(snapshotTag(sub))` yourself. Never put `'use cache'` inside PermDock calls, and never read `headers()` / `cookies()` in a function you mean to cache outside `'use cache: private'`:
27
28
 
@@ -41,18 +42,18 @@ export async function loadSnapshot(org: string) {
41
42
 
42
43
  Hooks under a `snapshotPromise` never suspend: they deny with `status: 'pending'` until it resolves, so render a skeleton for `pending` (or `<Protected pending>`); pass `suspend` to the provider only when readers should wait inside a Suspense boundary.
43
44
 
44
- After a role change: `updateTag(snapshotTag(user))` in the Server Action; `revalidateTag(tag, { expire: 0 })` in a Route Handler (not `'max'`, which would keep serving the revoked grant). Pages whose permission UI must be instant export `instant = true`; a rarely visited admin page exports `prefetch = 'force-disabled'`. Resource links that should carry their gated actions use `<Link prefetch={true}>` with the check in a `'use cache: private'` function keyed on the resource id. In `proxy.ts`, use `mayAccess(policy, claims, permission, { tenant })` (optimistic, never a decision). Both reach only the acting browser; for other members, add an app-owned signal (Realtime, poll, SSE) that calls `router.refresh()`. Keep the `[org]` layout synchronous and never read `cookies()` outside the private-cached loader. With a slug in the URL (`[orgSlug]`), resolve it to the org id in a `'use cache'` lookup that reads no session, call `notFound()` for an unknown slug, and pass the id to `snapshotFor`, `requireAccess` and `getPermDock`. Export `const { POST, GET } = permdockHandler()` from `app/api/permdock/route.ts`, or pass `endpoint: false` when every client check is portable; then a closure grant read by `usePermission` is denied with reason `server-only` (`permdock doctor` PD044 warns). Guide: [Next.js Cache Components](https://permdock.com/docs/guides/next-cache-components).
45
+ After a role change: `updateTag(snapshotTag(user))` in the Server Action; `revalidateTag(tag, { expire: 0 })` in a Route Handler (not `'max'`, which would keep serving the revoked grant). Pages whose permission UI must be instant export `instant = true`; a rarely visited admin page exports `prefetch = 'force-disabled'`. Resource links that should carry their gated actions use `<Link prefetch={true}>` with the check in a `'use cache: private'` function keyed on the resource id. In `proxy.ts`, use `mayAccess(policy, claims, permission, { tenant })` (optimistic, never a decision). Both reach only the acting browser; for other members, add an app-owned signal (Realtime, poll, SSE) that calls `router.refresh()`. Keep the `[org]` layout synchronous and never read `cookies()` outside the private-cached loader. With a slug in the URL (`[orgSlug]`), resolve it to the org id in a `'use cache'` lookup that reads no session, call `notFound()` for an unknown slug, and pass the id to `snapshotFor`, `requirePermission` and `getPermDock`. Export `const { POST, GET } = permdockHandler()` from `app/api/permdock/route.ts`, or pass `endpoint: false` when every client check is portable; then a closure grant read by `usePermission` is denied with reason `server-only` (`permdock doctor` PD044 warns). Guide: [Next.js Cache Components](https://permdock.com/docs/guides/next-cache-components).
45
46
 
46
47
  ## Hono — `permdock/hono`
47
48
 
48
49
  ```ts
49
50
  import { createPermDock } from "permdock/hono";
50
51
 
51
- export const { permdock, protect, permdockHandler } = createPermDock(policy, {
52
+ export const { middleware, protect, permdockHandler } = createPermDock(policy, {
52
53
  subject: (c) => c.get("user"),
53
54
  });
54
55
 
55
- app.use("*", permdock());
56
+ app.use("*", middleware());
56
57
  app.delete(
57
58
  "/posts/:id",
58
59
  protect(permissions.post.delete, (c) => loadPost(c)),
@@ -60,7 +61,7 @@ app.delete(
60
61
  );
61
62
  ```
62
63
 
63
- Every HTTP adapter takes `tenant` (for example `(c) => c.req.param('org')`), resolved again on each `protect` where route params exist, plus `limits` (a `LimitStore` for quota grants) and `pdp` (`createPermDock` from `permdock/pdp`). A `protect` loader's result is validated against the resource schema; pass `{ trusted: true }` as the third argument only when the loader returns a row the server loaded itself. `assert` inside a handler becomes the same 403 Problem Details as a guard denial; no `onError` wiring is needed.
64
+ Every HTTP adapter takes `tenant` (for example `(c) => c.req.param('org')`), resolved again on each `protect` where route params exist, plus `limits` (`limits(store)` over a `LimitStore`, for quota grants) and `pdp` (`getPermDock` from `permdock/pdp`). A `protect` loader's result is validated against the resource schema; pass `{ trusted: true }` as the third argument only when the loader returns a row the server loaded itself. `assert` inside a handler becomes the same 403 Problem Details as a guard denial; no `onError` wiring is needed.
64
65
 
65
66
  Streams and sockets: pass `revocations: memoryRevocationFeed()` (from `permdock`) and open a connection after `protect` succeeded. `sse` drops items the subscriber cannot read and ends with an `event: permdock` frame on revocation; await it last. `socket` closes a WebSocket with `1008`. Check inbound socket messages with `conn.check(permission, data)`, which validates the message data; a denial keeps the socket open.
66
67
 
@@ -84,7 +85,7 @@ app.get(
84
85
 
85
86
  Publish `revocations.revoke({ principal, tenant, kind: 'changed' })` from your role-edit code so open connections revalidate.
86
87
 
87
- For a framework loader (React Router, TanStack Start, SvelteKit, Nuxt), `createPermDock` from `permdock/server` returns `getSnapshot(request, { tenant })`: the snapshot a client provider takes, built from the request's own subject. Send it with `snapshotHeaders(snapshot)` from `permdock/server`, which sets a private `Cache-Control` from the snapshot's expiry. Docs: [loader snapshots](https://permdock.com/docs/adapters/server-kernel).
88
+ For a React Router loader, `createPermDock` from `permdock/server` returns `getSnapshot(request, { tenant })`: the snapshot a client provider takes, built from the request's own subject. Send it with `snapshotHeaders(snapshot)` from `permdock/server`, which sets a private `Cache-Control` from the snapshot's expiry. Docs: [loader snapshots](https://permdock.com/docs/adapters/server-kernel).
88
89
 
89
90
  ## React (Vite) — `permdock/react`
90
91
 
@@ -104,12 +105,12 @@ import { createPermDock } from "permdock/authzen";
104
105
  export const { permdockHandler } = createPermDock(policy, {
105
106
  subject: fromBearer,
106
107
  resources: {
107
- post: { load: (id) => loadPost(id), list: () => listPosts() },
108
+ post: { data: (id) => loadPost(id), list: () => listPosts() },
108
109
  },
109
110
  });
110
111
  ```
111
112
 
112
- One Fetch handler serves `POST /access/v1/evaluation`, `/evaluations`, `/search/action`, `/search/resource`, `/search/subject` and `GET /.well-known/authzen-configuration`. PEP authentication is required (`401` unless `anonymous: true`). The body `subject` is the evaluation principal when the PEP is trusted (default). Unknown actions return `decision: false` with `context.reason: 'unknown-permission'`. Omit `subjects.list` to drop search/subject from discovery. A custom PDP that evaluates delegated callers outside `createPermDock` calls `coveredByDelegation(permission, delegation, resourceId, hasActor)` from `permdock` instead of re-implementing scope, `authorization_details` and GNAP `access` matching.
113
+ `permdockHandler()` returns one Fetch handler that serves `POST /access/v1/evaluation`, `/evaluations`, `/search/action`, `/search/resource`, `/search/subject` and `GET /.well-known/authzen-configuration`. PEP authentication is required (`401` unless `anonymous: true`). The body `subject` is the evaluation principal when the PEP is trusted (default). Unknown actions return `decision: false` with `context.reason: 'unknown-permission'`. Omit `subjects.list` to drop search/subject from discovery. A custom PDP that evaluates delegated callers outside `createPermDock` calls `coveredByDelegation(permission, delegation, resourceId, hasActor)` from `permdock` instead of re-implementing scope, `authorization_details` and GNAP `access` matching.
113
114
 
114
115
  ## OpenAPI — `permdock/openapi`
115
116
 
@@ -138,18 +139,18 @@ import { PermDockProvider, usePermission } from "permdock/react-native";
138
139
 
139
140
  To answer guards from synced rows, write `localSnapshotManifest(policy)` (from `permdock`) to a JSON file at build time and pass `source={localSnapshot({ manifest, read, subscribe })}`. `read()` returns `{ principal: { id, tenant, roles, memberships, attributes }, customRoles }` from the local database; `subscribe` re-reads on row changes. It decides like the server snapshot except relation grantees (server-only) and the assignable lists (empty). Never import `policy.ts` into the app for this. Docs: [local snapshot](https://permdock.com/docs/adapters/react-native#local-snapshot).
140
141
 
141
- For an offline app on PowerSync, set `powersync: { out: 'sync-config.yaml' }` in `permdock.config.ts` and run `permdock powersync generate`: one edition 3 Sync Stream per resource, from the `rls.tables` and `rls.memberships` the RLS generator reads. It under-syncs, never over-syncs: a resource with a deny grant gets no stream, and a grant with a global role, approval, break-glass, validity, request context or a condition without a Sync Streams form syncs nothing, each with a warning. The app reads what does not sync from the server. It also writes `permdock_*` streams for the user's own membership, global-role and custom-role rows; with `powersync.manifest` set it writes the manifest too, and `powersyncSource(db, { manifest, queries, read })` from `permdock/react-native` builds the local snapshot from `db.watch` queries. `permdock powersync verify --db $DATABASE_URL` fails when a stream holds a fixture row the policy denies; doctor PD058 flags a stale file or manifest. Doctor PD065 lists server-only grants read from React Native code, which the device denies offline. Docs: [`permdock powersync`](https://permdock.com/docs/cli/powersync).
142
+ For an offline app on PowerSync, set `powersync: { out: 'sync-config.yaml' }` in `permdock.config.ts` and run `permdock powersync generate`: one edition 3 Sync Stream per resource, from the `rls.tables` and `rls.memberships` the RLS generator reads. It under-syncs, never over-syncs: a resource with a deny grant gets no stream, and a grant with a global role, approval, break-glass, validity, request context or a condition without a Sync Streams form syncs nothing, each with a warning. The app reads what does not sync from the server. It also writes `permdock_*` streams for the user's own membership, global-role and custom-role rows; with `powersync.manifest` set it writes the manifest too, and `powerSyncSource(db, { manifest, queries, read })` from `permdock/react-native` builds the local snapshot from `db.watch` queries. `permdock powersync verify` fails when a stream holds a fixture row the policy denies; doctor PD058 flags a stale file or manifest. Doctor PD065 lists server-only grants read from React Native code, which the device denies offline. Docs: [`permdock powersync`](https://permdock.com/docs/cli/powersync).
142
143
 
143
144
  ## Express — `permdock/express`
144
145
 
145
146
  ```ts
146
147
  import { createPermDock } from "permdock/express";
147
148
 
148
- export const { permdock, protect, errorHandler } = createPermDock(policy, {
149
+ export const { middleware, protect, errorHandler } = createPermDock(policy, {
149
150
  subject: (req) => req.user ?? null,
150
151
  });
151
152
 
152
- app.use(permdock());
153
+ app.use(middleware());
153
154
  app.delete(
154
155
  "/posts/:id",
155
156
  protect(permissions.post.delete, (req) => loadPost(req.params.id)),
@@ -158,18 +159,18 @@ app.delete(
158
159
  app.use(errorHandler());
159
160
  ```
160
161
 
161
- Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. A PermDock error thrown by `permdock()`, `protect` or the decision routes becomes Problem Details; any other error goes to `next(err)`.
162
+ Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. A PermDock error thrown by `middleware()`, `protect` or the decision routes becomes Problem Details; any other error goes to `next(err)`.
162
163
 
163
164
  ## Fastify — `permdock/fastify`
164
165
 
165
166
  ```ts
166
167
  import { createPermDock } from "permdock/fastify";
167
168
 
168
- export const { permdock, protect } = createPermDock(policy, {
169
+ export const { middleware, protect } = createPermDock(policy, {
169
170
  subject: (request) => request.user ?? null,
170
171
  });
171
172
 
172
- await app.register(permdock);
173
+ await app.register(middleware());
173
174
  app.delete(
174
175
  "/posts/:id",
175
176
  {
@@ -188,11 +189,11 @@ Registers a `fastify-plugin`-style root plugin (`skip-override`) that decorates
188
189
  ```ts
189
190
  import { createPermDock } from "permdock/elysia";
190
191
 
191
- export const { permdock, protect } = createPermDock(policy, {
192
+ export const { middleware, protect } = createPermDock(policy, {
192
193
  subject: ({ store }) => store.user ?? null,
193
194
  });
194
195
 
195
- const app = new Elysia().use(permdock()).delete("/posts/:id", handler, {
196
+ const app = new Elysia().use(middleware()).delete("/posts/:id", handler, {
196
197
  beforeHandle: protect(permissions.post.delete, ({ params }) =>
197
198
  loadPost(params.id),
198
199
  ),
@@ -201,6 +202,59 @@ const app = new Elysia().use(permdock()).delete("/posts/:id", handler, {
201
202
 
202
203
  Fetch-native plugin via `derive`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. For `.ws` routes, `connection(ws, { permission })` in `open` returns the socket's connection (memoised); it closes the socket with `1008` when revoked.
203
204
 
205
+ ## Meta-framework servers — `permdock/sveltekit`, `permdock/astro`, `permdock/solid-start`, `permdock/tanstack-start`, `permdock/h3`
206
+
207
+ Each `createPermDock` returns `{ middleware, protect, getPermDock, getSnapshot, connection, permdockHandler, openapi }`; all but TanStack Start add `withPermDock(handler)`, which types the instance for a handler without a check and answers a thrown PermDock error with Problem Details. `protect(target, data?, options?)` takes a permission, an operation from `defineOperations` or `null`; a loader that returns `null` or `undefined` answers `404`. The kernel caches one instance per request and tenant, so `middleware()` plus `protect` builds one. Nitro and Nuxt server routes are h3 handlers and use `permdock/h3`; there is no `permdock/nitro` or `permdock/nuxt`.
208
+
209
+ | Entry | `subject` receives | `middleware()` goes in | Instance and row land on | Docs |
210
+ | ------------------------- | ------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
211
+ | `permdock/sveltekit` | `RequestEvent` | `export const handle` in `src/hooks.server.ts` | `event.locals.permdock`, `event.locals.permdockData` | [SvelteKit](https://permdock.com/docs/adapters/sveltekit) |
212
+ | `permdock/astro` | `APIContext` | `export const onRequest` in `src/middleware.ts` | `context.locals.permdock`, `context.locals.permdockData` | [Astro](https://permdock.com/docs/adapters/astro) |
213
+ | `permdock/solid-start` | the h3 event under the request | `createMiddleware([middleware()])` in `src/middleware.ts` | `event.context.permdock` (middleware); `event.locals.permdock`, `permdockData` (API routes) | [SolidStart](https://permdock.com/docs/adapters/solid-start) |
214
+ | `permdock/tanstack-start` | the Fetch `Request` | `requestMiddleware` in `createStart` | middleware `context.permdock`, `context.permdockData` | [TanStack Start](https://permdock.com/docs/adapters/tanstack-start) |
215
+ | `permdock/h3` | `H3Event` | `app.use(middleware())` | `event.context.permdock`, `event.context.permdockData` | [h3](https://permdock.com/docs/adapters/h3) |
216
+
217
+ ```ts
218
+ // SvelteKit: src/hooks.server.ts, then src/routes/api/posts/[id]/+server.ts
219
+ export const handle = middleware();
220
+ export const PATCH = protect(permissions.post.update, (event) =>
221
+ loadPost(event.params.id),
222
+ )((event) => Response.json(event.locals.permdockData));
223
+
224
+ // Astro: src/middleware.ts; endpoints wrap like SvelteKit with context.locals
225
+ export const onRequest = middleware();
226
+
227
+ // SolidStart: src/middleware.ts, registered with solidStart({ middleware: "./src/middleware.ts" })
228
+ export default createMiddleware([middleware()]);
229
+
230
+ // TanStack Start: src/start.ts, then a server route
231
+ export const startInstance = createStart(() => ({
232
+ requestMiddleware: [middleware()],
233
+ }));
234
+ export const Route = createFileRoute("/api/posts/$id")({
235
+ server: {
236
+ middleware: [protect(permissions.post.update, postFromPath)],
237
+ handlers: { PATCH: ({ context }) => Response.json(context.permdockData) },
238
+ },
239
+ });
240
+
241
+ // h3 (Nitro, Nuxt server routes)
242
+ app.use(middleware());
243
+ app.patch(
244
+ "/posts/:id",
245
+ withPermDock<Post>((event) => update(event.context.permdockData)),
246
+ {
247
+ middleware: [
248
+ protect(permissions.post.update, (event) =>
249
+ loadPost(event.context.params?.id),
250
+ ),
251
+ ],
252
+ },
253
+ );
254
+ ```
255
+
256
+ The decision endpoint is `export const { GET, POST } = permdockHandler()` (SvelteKit, Astro, SolidStart), `server: { handlers: permdockHandler() }` (TanStack Start) or `app.all("/api/permdock", permdockHandler())` (h3). Pages and forms call `getPermDock` and check with `can` or `assert`: a SvelteKit form action or `load` maps a thrown PermDock error to `error(403)`, an Astro page calls `getPermDock(Astro)` and redirects, a SolidStart server function calls `getPermDock(getRequestEvent())`, and a TanStack Start server function calls `getPermDock(getRequest())`. TanStack Start route params are not on the `Request`; parse them from `request.url`. Route `beforeLoad` and `loader` also run on the client, so they are never the guard. Astro needs `output: "server"` or `prerender = false` on the guarded route. `App.Locals` augmentation is left to the app.
257
+
204
258
  ## Nest — `permdock/nest`
205
259
 
206
260
  ```ts
@@ -222,7 +276,7 @@ export class AppModule {}
222
276
  ```ts
223
277
  import { createPermDock } from "permdock/node";
224
278
 
225
- export const { permdock, protect, send, permdockHandler } = createPermDock(
279
+ export const { getPermDock, protect, send, permdockHandler } = createPermDock(
226
280
  policy,
227
281
  {
228
282
  subject: (req) => userFromCookie(req.headers.cookie),
@@ -230,36 +284,36 @@ export const { permdock, protect, send, permdockHandler } = createPermDock(
230
284
  );
231
285
  ```
232
286
 
233
- Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. Express and Nest reuse `toRequest` / `fromResponse`. The body is read only when consumed, so a body parser after `permdock(req)` still works. There is no error hook: wrap handlers that call `assert` and `send(res, problemFromError(error))` (from `permdock/server`) when it returns a `Response`.
287
+ Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. Express and Nest reuse `toRequest` / `sendResponse`. The body is read only when consumed, so a body parser after `getPermDock(req)` still works. There is no error hook: wrap handlers that call `assert` and `send(res, problemFromError(error))` (from `permdock/server`) when it returns a `Response`.
234
288
 
235
289
  ## tRPC — `permdock/trpc`
236
290
 
237
291
  ```ts
238
292
  import { createPermDock } from "permdock/trpc";
239
293
 
240
- export const { permdock, protect, permdockHandler } = createPermDock(policy, {
294
+ export const { middleware, protect, permdockHandler } = createPermDock(policy, {
241
295
  subject: (opts) => opts.ctx.user ?? null,
242
296
  });
243
297
 
244
- const procedure = t.procedure.use(permdock());
298
+ const procedure = t.procedure.use(middleware());
245
299
  procedure
246
300
  .input(z.object({ id: z.string() }))
247
301
  .use(protect(permissions.post.delete, ({ input }) => loadPost(input.id)))
248
302
  .mutation(({ ctx }) => deletePost(ctx.permdockData));
249
303
  ```
250
304
 
251
- `protect` throws `TRPCError` (`FORBIDDEN`, `BAD_REQUEST`, `UNAUTHORIZED`) with Problem Details as `cause`; pass `errorFormatter` to `initTRPC.create` so clients see it under `data`. `ctx.permdock.assert` in a resolver maps to the same error. Read the tenant from each procedure's input (`tenant: (opts) => opts.input?.org`) so a batch never shares one; `request: (ctx) => ctx.req` names the Web `Request` when the context holds it elsewhere. `openapi.security(permission)` is the `trpc-to-openapi` meta fragment. On a subscription, `protect(permission, load, { items })` ends the iterator with `UNAUTHORIZED` or `FORBIDDEN` when the session is revoked or the permission re-denied, and drops items the subscriber cannot read; pass `revocations`.
305
+ `protect` throws `TRPCError` (`FORBIDDEN`, `BAD_REQUEST`, `UNAUTHORIZED`) with Problem Details as `cause`; pass `errorFormatter` to `initTRPC.create` so clients see it under `data`. `ctx.permdock.assert` in a resolver maps to the same error. Read the tenant from each procedure's input (`tenant: (opts) => opts.input?.org`) so a batch never shares one; `request: (ctx) => ctx.req` names the Web `Request` when the context holds it elsewhere. `permdockHandler()` returns the decision endpoint as a Fetch handler. `openapi.security(permission)` is the `trpc-to-openapi` meta fragment. On a subscription, `protect(permission, load, { items })` ends the iterator with `UNAUTHORIZED` or `FORBIDDEN` when the session is revoked or the permission re-denied, and drops items the subscriber cannot read; pass `revocations`.
252
306
 
253
307
  ## oRPC — `permdock/orpc`
254
308
 
255
309
  ```ts
256
310
  import { createPermDock } from "permdock/orpc";
257
311
 
258
- export const { permdock, protect, permdockHandler } = createPermDock(policy, {
312
+ export const { middleware, protect, permdockHandler } = createPermDock(policy, {
259
313
  subject: ({ context }) => context.user ?? null,
260
314
  });
261
315
 
262
- const base = os.$context<Context>().use(permdock());
316
+ const base = os.$context<Context>().use(middleware());
263
317
  base
264
318
  .input(z.object({ id: z.string() }))
265
319
  .use(protect(permissions.post.delete, ({ input }) => loadPost(input.id)))
@@ -313,6 +367,14 @@ instrument(permdock, {
313
367
 
314
368
  HTTP adapters accept the same options as `otel`. `@opentelemetry/api` is optional; without it the adapter writes only through `logger`.
315
369
 
370
+ ## Adapter hooks
371
+
372
+ Every HTTP adapter, `permdock/mcp` and `permdock/ai-sdk` take three hooks ([extend PermDock](https://permdock.com/docs/guides/extending)):
373
+
374
+ - `deniedResponse`: on HTTP, `({ decision, problem, request })` returns Problem Details with extra members, a `Response`, or `undefined`; a status below 300 or a throw keeps the default. On MCP and the agent adapters, `deniedText` returns replacement text only. On Next.js the factory `onDenied` throws or redirects, and also runs for `requirePermission`.
375
+ - `context`: returns plain JSON merged into `subject.context`. Return only server-derived values, never a header, body field or tool argument; it is visible in the snapshot.
376
+ - `wrap`: wraps each instance after `otel`; `permdock/next` takes it too. Build it with `wrapPermDock(permdock, (inner) => ({ … }))` so `tenant()`, `team()` and `derive()` stay wrapped.
377
+
316
378
  ## SCIM — `permdock/scim`
317
379
 
318
380
  No factory. Mount `scimHandler` and pass `directoryMembershipSource(store)` as `memberships`:
@@ -327,7 +389,7 @@ import {
327
389
 
328
390
  const directory = memoryDirectoryStore();
329
391
  export const scim = scimHandler({
330
- store: directory,
392
+ directory,
331
393
  tenant: (request) => tenantFromPath(request),
332
394
  token: { hash: "sha256", lookup: (tenant) => hashFor(tenant) },
333
395
  });
@@ -337,7 +399,7 @@ The handler writes users and groups. It never decides. Unknown or non-assignable
337
399
 
338
400
  ## Cloud — `permdock/cloud`
339
401
 
340
- No factory for a `PermDock`. `cloud({ url, key, environment })` returns `approvals`, `sink`, `snapshots` and `policies` to pass into any adapter, plus the environment `issuer` (`<url>/v1/environments/<environment>`) and its `jwks` URL; `cloudEndpoints({ url, environment })` computes the same two without a key. It never implements `MembershipSource` or `RoleSource` and never decides.
402
+ No factory for a `PermDock`. `cloud({ url, key, environment })` returns `approvals`, `sink` and `policies` to pass into any adapter, `snapshotsFor(token)` (a `SnapshotSource` for the subject an `at+jwt` proves, sent as `PermDock-Subject`), `catalog.push`, plus the environment `issuer` (`<url>/v1/environments/<environment>`) and its `jwks` URL; `cloudEndpoints({ url, environment })` computes the same two without a key. It never implements `MembershipSource` or `CustomRoleSource` and never decides.
341
403
 
342
404
  ```ts
343
405
  import { cloud } from "permdock/cloud";
@@ -354,22 +416,49 @@ export const { getPermDock } = createPermDock(policy, {
354
416
  });
355
417
  ```
356
418
 
357
- Hosted grants are opt-in per permission. List the permissions a Cloud admin may grant in `definePolicy(..., { hostable: [permissions.auditLog.read] })`, pass `verifier: joseTokenVerifier({ jwks: cloudEndpoints({ url, environment }).jwks })` to `cloud()` (the policy document's issuer and audience are both the environment URL), forward `policies: permdockCloud.policies`, and call `permdockCloud.policies.refresh()` on a timer. Never mark a permission `hostable` when `permdock rls` compiles its table (`permdock doctor` PD020).
419
+ Hosted grants are opt-in per permission. List the permissions a Cloud admin may grant in `definePolicy(..., { hostable: [permissions.auditLog.read] })`, pass `verifier: joseTokenVerifier({ jwks: cloudEndpoints({ url, environment }).jwks })` to `cloud()` (the policy document's issuer and audience are both the environment URL), forward `policies: hostedPolicies(permdockCloud.policies)`, and call `permdockCloud.policies.refresh()` on a timer. Never mark a permission `hostable` when `permdock rls` compiles its table (`permdock doctor` PD020).
420
+
421
+ An app that calls `permdockCloud.approvals.resolve` also passes `approver: { signer }`, a `TokenSigner` over the app's own key whose JWK Set the Cloud environment trusts. Each resolve then carries a `PermDock-Approver` RFC 9068 `at+jwt` for `verdict.by.principal.id`. Without it, `resolve` throws `approver-unauthenticated`, and a verdict with an actor throws `approver-not-eligible`. The token carries the approver's `roles`, `memberships` and `tenant`; pass `approver: { signer, permissions, relations }` (typically from `approverPermissions` and `approverRelations`) when a request's `approvers` names a `holder()` or `relation()`, because a fact the token lacks counts as not held. Never pass `cloudSigner` here: it refuses `at+jwt` ([approver token](https://permdock.com/docs/adapters/cloud#approver-token)).
358
422
 
359
- `PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` are server-only. Production `PERMDOCK_CLOUD_URL` is `https://api.permdock.com`. The dashboard is `https://app.permdock.com`; the read-only MCP server is `https://mcp.permdock.com`. A Cloud outage leaves directory memberships at their last synced state.
423
+ `PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` are server-only. Production `PERMDOCK_CLOUD_URL` is `https://permdock.com/api`, which is also the platform issuer `permdock login` signs in at. The dashboard is `https://app.permdock.com`; the read-only MCP server is `https://permdock.com/mcp`. `PERMDOCK_CLOUD_TOKEN` is a read-only CI key for `permdock cloud pull` and `permdock rls generate --project`; never deploy it with the app. A Cloud outage leaves directory memberships at their last synced state.
424
+
425
+ ## Delivery, chat and automation implementations — `permdock/resend`, `permdock/chat-sdk`, `permdock/workflow-sdk`
426
+
427
+ These entries fill the feature contracts `permdock/delivery`, `permdock/chat` and `permdock/workflow` with one vendor SDK each. None imports the SDK at runtime: pass in your own client or functions (`resend` 6, `chat` 4 and `workflow` are optional peers). Automations and chat bots are in the `permdock-agents` skill and approval prompts in the `permdock-approvals` skill; install each with `npx skills add ScaleDockHQ/PermDock --skill <name>`.
428
+
429
+ ```ts
430
+ import { chatSdkTransport } from "permdock/chat-sdk";
431
+ import { createPermDock } from "permdock/delivery";
432
+ import { resendTransport } from "permdock/resend";
433
+
434
+ const delivery = createPermDock(policy, {
435
+ transports: {
436
+ email: resendTransport(new Resend(process.env.RESEND_API_KEY), {
437
+ from: "Acme <billing@acme.test>",
438
+ }),
439
+ chat: chatSdkTransport(chat, { provider: "slack", workspace: "T0ACME" }),
440
+ },
441
+ principals,
442
+ sink,
443
+ });
444
+ ```
445
+
446
+ - **`permdock/resend`** ([Resend](https://permdock.com/docs/adapters/resend)): `resendTransport(resend, { from?, replyTo?, tags? })` is the email transport; it sends each recipient's idempotency key as Resend's `idempotencyKey`, fans out through `batch.send` and maps Resend errors to retryable or final failures (`resendFailure`). `approvalEmail(prompt, { approvalUrl, product? })` renders an approval prompt that links once to the approval page and has no approve or reject link. `resendWebhook({ resend, secret, sink, replay })` is a fetch handler that verifies the signature, claims each webhook id in a `ReplayStore` (`memoryReplayStore` from `permdock/server`) and writes delivered, bounced, complained, failed and suppressed events to the sink.
447
+ - **`permdock/chat-sdk`** ([Chat SDK](https://permdock.com/docs/adapters/chat-sdk)): `chatSdkAuthor(user, adapter, { workspace? })` turns a verified Chat SDK `Author` into a `ChatAuthor` from `userId` only, and returns `null` for bots and the platform. `chatSdkTransport(chat, { provider, workspace?, remember? })` posts direct messages, and `approvalCard(prompt, { approvalUrl, buttons? })` posts the approval prompt with a Review link and buttons that carry the request id, never the token. `approvalActions(permdock, { workspace?, onResult? })` applies those clicks as the clicking user through `ChatPermDock.resolveAction`: `chat.onAction([...actions.actionIds], actions.onAction)`.
448
+ - **`permdock/workflow-sdk`** ([Workflow SDK](https://permdock.com/docs/adapters/workflow-sdk)): `startAutomation(workflow, start, run, id, args, options?)` starts a Workflow SDK run only after `WorkflowPermDock.start` grants it, with the run ticket as the first argument. `workflowSdkWaiter({ createHook, resumeHook, sleep? })` is an `ApprovalWaiter` that parks a run on the hook `approvalHookToken(token)` until `waiter.wake(token)`. `authorizeHook(resumeHook, caller, permission, row, token, payload)` resumes a hook only when `caller` is granted the permission; a gone hook denies with `validation` (`cause: 'hook-unavailable'`).
360
449
 
361
450
  ## Better Auth — `permdock/better-auth`
362
451
 
363
452
  ```ts
364
453
  import {
365
454
  subjectFromBetterAuth,
366
- betterAuthRoleSource,
455
+ betterAuthCustomRoleSource,
367
456
  } from "permdock/better-auth";
368
457
 
369
458
  const session = await auth.api.getSession({ headers });
370
- const subject = await subjectFromBetterAuth(auth, session);
371
- const permdock = await createPermDock(policy, subject, {
372
- customRoles: betterAuthRoleSource(auth),
459
+ const subject = await subjectFromBetterAuth(session, { auth });
460
+ const permdock = await getPermDock(policy, subject, {
461
+ customRoles: betterAuthCustomRoleSource(auth),
373
462
  });
374
463
  ```
375
464
 
@@ -385,7 +474,7 @@ export const { getPermDock } = createPermDock(policy, {
385
474
  });
386
475
  ```
387
476
 
388
- Pass `auth()` or a verified session payload only. A plain `{ userId }` object is anonymous. `memberships: 'all'` loads organizations through the Clerk Backend API; create `createClerkSubjectResolver({ memberships: 'all', backend, cache: { ttl: '10s' } })` once per process to cache each user's list (at most 30 seconds, so a removed membership grants until then). `o:` plans and features from `pla` and `fea` hold only in the session organization.
477
+ Pass `auth()` or a verified session payload only. A plain `{ userId }` object is anonymous. `memberships: 'all'` loads organizations through the Clerk Backend API; create `clerkSubjectResolver({ memberships: 'all', backend, cache: { ttl: 10 } })` once per process to cache each user's list (at most 30 seconds, so a removed membership grants until then). `o:` plans and features from `pla` and `fea` hold only in the session organization.
389
478
 
390
479
  ## Supabase — `permdock/supabase`
391
480
 
@@ -393,16 +482,20 @@ Pass `auth()` or a verified session payload only. A plain `{ userId }` object is
393
482
  import { subjectFromSupabase, authorizeSql } from "permdock/supabase";
394
483
 
395
484
  const subject = subjectFromSupabase(claims, {
396
- roles: "user_role",
397
- tenant: "tenant_id",
398
- memberships: "memberships",
485
+ claims: {
486
+ roles: "user_role",
487
+ tenant: "tenant_id",
488
+ memberships: "memberships",
489
+ },
399
490
  declared: ["member", "admin"],
400
491
  });
401
492
  ```
402
493
 
403
- Write the claims with `permdock supabase hook generate`; `permdock supabase inspect --json` prints the manifest (helper names, tenant claim, budget, claims written) which `authorizationProvider` from `permdock/better-supabase` turns into better-supabase's `authorization` config. No `@supabase/supabase-js` peer. Pair with `permdock rls generate`, or keep SQL as authority and run `permdock rls verify --db` against `sqlFunction` twins ([database-first](https://permdock.com/docs/adapters/rls)); the generic RLS workflow is in the `permdock-data` skill.
494
+ Write the claims with `permdock supabase hook generate`; `permdock supabase inspect --out` writes the manifest (helper names, tenant claim, budget, claims written) that `authorizationProvider` from `permdock/better-supabase` turns into better-supabase's `authorization` config. No `@supabase/supabase-js` peer. Pair with `permdock rls generate`, or keep SQL as authority and run `permdock rls verify --db-url-stdin` against `sqlFunction` twins ([database-first](https://permdock.com/docs/adapters/rls)); the generic RLS workflow is in the `permdock-data` skill.
495
+
496
+ With better-supabase, set `authorization: authorizationProvider({ manifest, catalog })` in `better-supabase.config.ts` (with `rls.customRoles` its `canAssign` and `canAssignFor` call `permdock_can_assign_any` and its `_for` form, so custom roles are assignable through better-supabase; regenerate the manifest after turning custom roles on), pass `authorizer({ policy })` to the server (`createNext`, `createHono`, `createMcp`) so every `permission` a resource, action, route guard or tool declares is decided by PermDock from the verified claims (`operations` maps tables for `permissions: true`; a missing or anonymous claim denies), build bucket and topic `access` policies with `bucketPolicy` and `topicPolicy`, and map its session with `subjectFromBetterSupabase(session)` (all from `permdock/better-supabase`): the active tenant's `features` become `principal.plans`. Pass `plans: { keys }` when better-supabase's `entitlements.claim.keys` shortens feature keys, and `apiKeys: { permissions, manifest }` so an `apiKey` session maps to the key's credential subject instead of the anonymous one. For `createMcp` tools, put the permission in `meta` and spread `toolPolicy({ permdock })` into the options: tools without one are hidden and refused, and approval-required calls are refused because `createMcp` collects no approvals. Wrap a `CredentialProvider` with `credentialGuard(provider, { permdock, use, revoke? })` so PermDock decides each token use. `authorizationProvider({ ..., approver })` lets holders of that permission approve AI tool calls, never their own. `approvers: { 'ai-tool': holder(p), 'support-session': ..., 'invoice.send': ... }` fills `isApprover` through `permdock_is_approver` (needs `rls.approvals`); only `user()`, `holder()` and `anyOf()` approvers, and a kind it does not name has no approver. `accessReviewApplier({ client, reviews, policy, demoteTo?, email?, permdockFor? })` applies a review's revoked items: `update_member_role` to the `demoteTo` role when that role lacks the permission, else `remove_member`, plus `revoke_invitation` for the member's matching invitations, then `markApplied`; pass the reviewer's client so the blocks' own checks run. better-supabase's `createAgentRuntime({ toolApproval })` takes `toolApproval` from `permdock/ai-sdk` as it is. Validate its claims with `betterSupabase.claims(supabaseClaims().extend(appSchema))` (`supabaseClaims` from `permdock/supabase`, any Standard Schema as `appSchema`) instead of a hand-written copy of the claim shape. A `supabase.hook.claims` function that needs the user's organizations calls `<schema>.member_organization_ids_for(user_id)` (generated by `rls generate`, executable by `supabase_auth_admin` only) instead of querying the membership tables itself. A token Supabase's OAuth server issued to a third-party app (`client_id`) becomes an `oauth-client` actor limited to its `scope`; `deny(permission, { to: actor('oauth-client') })` keeps such apps out, in the app and, through `rls generate`, in Postgres. A better-supabase support session (`act.kind: 'support'`) is actor kind `support` with `sessionId` and `readOnly`, and `actingAs` (`act.kind: 'impersonation'`) is kind `impersonation`; neither has a delegation, so they reach nothing until a policy delegation names `actor('support')` or `actor('impersonation')`, and `readOnly: true` (also when `read_only` is absent) narrows that to read-only permissions. Generated RLS reads memberships, not `act`, so a support token sent to the Data API writes as the user: add a restrictive `for update` (and `insert` / `delete`) policy per writable table that requires `act ->> 'read_only' = 'false'` when `act` is a support or session level ([support access](https://permdock.com/docs/guides/support-access)). With `rls.anonymousSignIns: 'deny'`, pass `anonymousSignIns: 'deny'` to the subject mapper too (doctor PD057). Check a deployed database against the generated policies with `permdock rls verify --introspect`.
404
497
 
405
- With better-supabase, set `authorization: authorizationProvider({ manifest, catalog })` in `better-supabase.config.ts` (with `rls.customRoles` its `canAssign` and `canAssignFor` call `permdock_can_assign_any` and its `_for` form, so custom roles are assignable through better-supabase; regenerate the manifest after turning custom roles on), build bucket and topic `access` policies with `bucketPolicy` and `topicPolicy`, and map its session with `subjectFromBetterSupabase(session)` (all from `permdock/better-supabase`): the active tenant's `features` become `principal.plans`. Pass `plans: { keys }` when better-supabase's `entitlements.claim.keys` shortens feature keys, and `apiKeys: { permissions, manifest }` so an `apiKey` session maps to the key's credential subject instead of the anonymous one. For `createMcp` tools, put the permission in `meta` and spread `toolPolicy({ permdock })` into the options: tools without one are hidden and refused, and approval-required calls are refused because `createMcp` collects no approvals. Wrap a `CredentialProvider` with `credentialGuard(provider, { permdock, use, revoke? })` so PermDock decides each token use. `authorizationProvider({ ..., approver })` lets holders of that permission approve AI tool calls, never their own. better-supabase's `createAgentRuntime({ toolApproval })` takes `toolApproval` from `permdock/ai-sdk` as it is. Validate its claims with `betterSupabase.claims(supabaseClaims().extend(appSchema))` (`supabaseClaims` from `permdock/supabase`, any Standard Schema as `appSchema`) instead of a hand-written copy of the claim shape. A `supabase.hook.claims` function that needs the user's organizations calls `<schema>.member_organization_ids_for(user_id)` (generated by `rls generate`, executable by `supabase_auth_admin` only) instead of querying the membership tables itself. A token Supabase's OAuth server issued to a third-party app (`client_id`) becomes an `oauth-client` actor limited to its `scope`; `deny(permission, { to: actor('oauth-client') })` keeps such apps out, in the app and, through `rls generate`, in Postgres. A better-supabase support session (`act.kind: 'support'`) is actor kind `support` with `sessionId` and `readOnly`, and `actingAs` (`act.kind: 'impersonation'`) is kind `impersonation`; neither has a delegation, so they reach nothing until a policy delegation names `actor('support')` or `actor('impersonation')`, and `readOnly: true` (also when `read_only` is absent) narrows that to read-only permissions. Generated RLS reads memberships, not `act`, so a support token sent to the Data API writes as the user: add a restrictive `for update` (and `insert` / `delete`) policy per writable table that requires `act ->> 'read_only' = 'false'` when `act` is a support or session level ([support access](https://permdock.com/docs/guides/support-access)). With `rls.anonymousSignIns: 'deny'`, pass `anonymousSignIns: 'deny'` to the subject mapper too (doctor PD057). Check a deployed database against the generated policies with `permdock rls verify --introspect --db $DATABASE_URL`.
498
+ The other better-supabase slots, all from `permdock/better-supabase` ([better-supabase](https://permdock.com/docs/adapters/better-supabase)): `hookCan(permdock)` and `handlerCan({ getPermDock })` are the `can` options of `authorizeHook` and `protectWebHandler` in `better-supabase/workflow-sdk`; they decide with no row, so a permission with a row condition denies there and is checked inside the handler, and `handlerCan` answers `undefined` (401) without a signed-in principal. `supportPolicy({ policy, options?, writable?, approval?, approved? })` is the `authorize` option of `supportSessions`; it decides each start with `decideSupportSession` as the admin, and an `approval-required` start passes only once `approved(input, pending)` returns `{ approvedBy }` for an approver other than the admin. `subscriberPolicy({ policy, principals, events, row?, sender? })` filters outbox, webhook or realtime subscribers to those who may read the event (exact type, then the longest `prefix.*`, then `*`; a type with no entry reaches no one). `principalSource({ claims, identities? })` is a `PrincipalSource` over the claims better-supabase mints, for `decideFor`, `subscribersFor` and delivery; only links the app created through a signed-in flow or a verified address go in `identities`. `authorizationHooks({ policy, claims, service?, principals?, events?, approvers?, support?, tickets? })` is the `authorization` option of `createServer`: it answers automation publish and run-as, delegated OAuth scopes, `approverOf`, identities, delivery audiences, support sessions and run tickets from the claims better-supabase mints now, never from the request.
406
499
 
407
500
  `claims` is whatever Supabase verified: `data.claims` from `supabase.auth.getClaims()` on an `@supabase/ssr` server client, or `jwtClaims` from `@supabase/server` (`ctx.jwtClaims` in `withSupabase` or a `pipeline`, and `c.var.jwtClaims` after a framework bridge such as `toHono([withClaims(), withPermDock()])`). `@supabase/server/adapters/*` and `c.var.supabaseContext` are deprecated and removed on 1 December 2026; do not write new code against them ([Hono, H3, Elysia and NestJS](https://permdock.com/docs/adapters/supabase#hono-h3-elysia-and-nestjs)). Pass `null` for anonymous callers; never `getSession().access_token` (unverified). An API-key auth mode (`secret`, `publishable`) has `jwtClaims: null` and is the anonymous subject, not a user.
408
501
 
@@ -411,14 +504,15 @@ Share links reach RLS through `exchangeCapability`; see the `permdock-credential
411
504
  Supabase specifics for generated RLS (the generic flags and parity checks are in the `permdock-data` skill):
412
505
 
413
506
  - Point `--seeds-out` at the migrations directory (`--seeds-out supabase/migrations/`) so `rls generate` writes a new timestamped seeds migration only when the `role_permissions` rows change; never rewrite an applied seeds migration in place.
414
- - With declarative schemas on pg-delta (`[experimental.pgdelta] enabled = true`), run `permdock rls generate --target sql --split helpers,seeds,indexes,policies,hook --seeds-out supabase/migrations/<timestamp>_permdock_seeds.sql` with no `--out`: it writes `supabase/schemas/permdock/{helpers,indexes}.sql`, `public/policies/permdock.sql` and `permdock/functions/custom_access_token_hook.sql`, grants included. Then `supabase db schema declarative sync -f <name>` writes the migration; the seeds migration must sort after it. Never put the `role_permissions` rows in a schema file: pg-delta rejects data.
415
- - With `supabase db diff` instead, generate `--split helpers,seeds,policies,hook --out supabase/schemas/identity/056_permdock_{part}.sql` and write the schema and function privileges and the `security_invoker` view option with `--grants-out` into a migration created by `supabase migration new` after the first `db diff`; `db diff` drops those, so never append them to a migration by hand. Table grants and policies stay in the schema files, where `db diff` keeps them. Keep the helpers part ahead of every file that calls the helpers in `schema_paths` (`permdock doctor` PD042, PD043).
507
+ - Declarative Schemas 2.0 (pg-delta) is the default. Set `[experimental.pgdelta] enabled = true` in `supabase/config.toml` (Supabase CLI 2.119 or later), then run `permdock rls generate --target sql --split helpers,seeds,indexes,policies,hook --seeds-out supabase/migrations/` with no `--out`: it writes `supabase/schemas/permdock/{helpers,indexes}.sql`, `public/policies/permdock.sql` and `permdock/functions/custom_access_token_hook.sql`, grants included. `supabase db schema declarative sync --no-apply -f <name>` writes the migration; the seeds migration must sort after it, so on the first run print the seeds (`--seeds-out -`), sync, then generate again. Never put the `role_permissions` rows in a schema file: pg-delta rejects data. Never pass `--grants-out` here; it is refused. `permdock doctor` PD068 fails while `config.toml` lacks `enabled = true`, and PD069 lists `schema_paths` and numbered parts left over from migra.
508
+ - Opt out only for a project that is not on pg-delta: `supabase.schemas: 'migra'` (or `[experimental.pgdelta] enabled = false`) for `supabase db diff`, `supabase.schemas: 'migrations'` for hand-written migrations. `--schemas` overrides both for one run ([schema workflow](https://permdock.com/docs/cli/rls#schema-workflow)).
509
+ - Under `migra`, generate `--split helpers,seeds,policies,hook --out supabase/schemas/identity/056_permdock_{part}.sql --seeds-out supabase/migrations/` and write the schema and function privileges and the `security_invoker` view option with `--grants-out` into a migration created by `supabase migration new` after the first `db diff`; `db diff` drops those, so never append them to a migration by hand. Table grants and policies stay in the schema files, where `db diff` keeps them. Keep the helpers part ahead of every file that calls the helpers in `schema_paths` (`permdock doctor` PD042, PD043).
416
510
  - `--authorize jwt` reads the hook's claims: enable the printed `[auth.hook.custom_access_token]` stanza in `supabase/config.toml`. The generated hook writes the canonical `memberships` claim from the `rls.memberships.scopes` tables, so do not hand-write one.
417
- - For more than one membership table, declare `fromTable` / `fromJunction` sources once (from `permdock/supabase`, with a `query` on the server), pass the array as `memberships` (wrapped in `claimsFirst(sources, { version: authzVersion({ query }) })` to trust the token until it is truncated or stale), put the same sources under `supabase.hook.memberships` in `permdock.config.ts`, and run `permdock supabase hook generate`; add the printed `config.toml` block (`jwt_expiry = 900`). List member-removal and payout permissions in `definePolicy({ fresh })`.
511
+ - For more than one membership table, declare `tableMembershipSource` / `junctionMembershipSource` sources once (from `permdock/supabase`, with a `query` on the server), pass the array as `memberships` (wrapped in `claimsFirst(sources, { version: authzVersion({ query }) })` to trust the token until it is truncated or stale), put the same sources under `supabase.hook.memberships` in `permdock.config.ts`, and run `permdock supabase hook generate`; add the printed `config.toml` block (`jwt_expiry = 900`). List member-removal and payout permissions in `definePolicy({ fresh })`.
418
512
  - When the server has only supabase-js (no `SqlQuery`), never re-read memberships, global roles or custom roles with the admin client: grant `subject_for(p_user)` and `authz_version_for(p_user)` (written next to the hook) to the backend role, expose them through `public` wrappers when `permdock` is not an exposed schema, and use `postgrestSources(admin, { schema, fn, versionFn, policy })`: `claimsFirst(sources.memberships)` (with `onStale: 'reread'` a fresh token costs one version call, and with `policy` a token that claims a custom role costs one `subject_for` call), `customRoles: sources.customRoles(principal)`, and `await sources.subject(userId)` for a job acting for a stored user.
419
- - When a role table stores a role id (`user_roles.role_id`, `organization_users.role_id` referencing `roles`), read the key through it with `{ through: 'roles', on: { role_id: 'id' }, column: 'key' }`: in `rls.roles`, in `fromTable` `columns.role` or `fromJunction` `roles`, and in an `rls.memberships` table's `role`. Never copy keys into the membership table. Keep tenant custom-role keys apart from declared role names.
420
- - When one membership row holds roles in several columns (a `tier` and a `role_id`), list them instead of building a union view: `role: ['tier', { through: 'roles', on: { role_id: 'id' }, column: 'key' }]` in `rls.memberships` and `fromTable` `columns.role`, `roles: { sources: [...] }` in `fromJunction`. The row holds every non-null key, and the holder-count triggers stay on the real table.
421
- - When a membership table names a profile instead of a login (`customer_contacts.contact_profile_id` referencing `contact_profiles`, whose `user_id` is the login), read the user through it with `fromJunction` `user` or `fromTable` `columns.user`: `{ through: 'contact_profiles', on: { contact_profile_id: 'id' }, column: 'user_id' }`. Never copy `user_id` onto the membership rows. Keep `contact_profiles.user_id` server-owned (PD028).
513
+ - When a role table stores a role id (`user_roles.role_id`, `organization_users.role_id` referencing `roles`), read the key through it with `{ through: 'roles', on: { role_id: 'id' }, column: 'key' }`: in `rls.roles`, in `tableMembershipSource` `columns.role` or `junctionMembershipSource` `roles`, and in an `rls.memberships` table's `role`. Never copy keys into the membership table. Keep tenant custom-role keys apart from declared role names.
514
+ - When one membership row holds roles in several columns (a `tier` and a `role_id`), list them instead of building a union view: `role: ['tier', { through: 'roles', on: { role_id: 'id' }, column: 'key' }]` in `rls.memberships` and `tableMembershipSource` `columns.role`, `roles: { sources: [...] }` in `junctionMembershipSource`. The row holds every non-null key, and the holder-count triggers stay on the real table.
515
+ - When a membership table names a profile instead of a login (`customer_contacts.contact_profile_id` referencing `contact_profiles`, whose `user_id` is the login), read the user through it with `junctionMembershipSource` `user` or `tableMembershipSource` `columns.user`: `{ through: 'contact_profiles', on: { contact_profile_id: 'id' }, column: 'user_id' }`. Never copy `user_id` onto the membership rows. Keep `contact_profiles.user_id` server-owned (PD028).
422
516
  - For attribute conditions (`principal.claims.attrs.region`), list server-owned columns or `app_metadata.<key>` in `supabase.hook.attrs`, never `user_metadata`, and keep clients from updating those columns (`permdock doctor` PD028).
423
517
 
424
518
  ## Supabase middleware pipeline — `permdock/supabase/middleware`
@@ -432,7 +526,9 @@ import { createPermDock } from "permdock/supabase/middleware";
432
526
  const { withPermDock, permdockHandler } = createPermDock(policy, {
433
527
  subject: (ctx) =>
434
528
  subjectFromSupabase(ctx.jwtClaims, {
435
- roles: "user_role",
529
+ claims: {
530
+ roles: "user_role",
531
+ },
436
532
  declared: ["member", "admin"],
437
533
  }),
438
534
  });
@@ -467,7 +563,7 @@ pipeline([withClaims()], permdockHandler());
467
563
  ## Remote PDP — `permdock/pdp`
468
564
 
469
565
  ```ts
470
- import { createPermDock, remotePdp } from "permdock/pdp";
566
+ import { getPermDock, remotePdp } from "permdock/pdp";
471
567
 
472
568
  const policy = definePolicy(permissions, {
473
569
  roles: [member],
@@ -477,16 +573,16 @@ const policy = definePolicy(permissions, {
477
573
  ],
478
574
  });
479
575
 
480
- const permdock = await createPermDock(policy, user);
576
+ const permdock = await getPermDock(policy, user);
481
577
  await permdock.can(permissions.post.read, post);
482
578
  ```
483
579
 
484
- Use `createPermDock` from `permdock/pdp` when `providers` is set. Core `can` / `decide` stay synchronous and deny delegated permissions with `pdp-unavailable`; on the PDP instance `filter` and `where` are async too.
580
+ Use `getPermDock` from `permdock/pdp` when `providers` is set. Core `can` / `decide` stay synchronous and deny delegated permissions with `pdp-unavailable`; on the PDP instance `filter` and `where` are async too.
485
581
 
486
582
  For OpenFGA or SpiceDB, use `openfga({ url, storeId, map })` or `spicedb({ url, token, map })` as the provider. `map` is one `[permission, (subject, row) => tuple]` pair per delegated permission; `filter` and `where` then use `list-objects` / `LookupResources` ids, so the tuple ids must equal the resource's `id` field. The app writes the tuples.
487
583
 
488
584
  ## Collect-only frameworks (no package)
489
585
 
490
- Nuxt, Astro, React Router, TanStack Start and Effect have no `permdock/<name>` entry. Add `createPermDockUnplugin.vite()` from `permdock/unplugin` (or `.webpack` / `.esbuild`). Runtime is `permdock/server` or the matching HTTP adapter, plus `permdock/vue`, `permdock/react` or `permdock/svelte` on the client. Effect Schema is a Standard Schema; Effect HttpApi uses Overlay, not a hook.
586
+ Nuxt, React Router and Effect have no `permdock/<name>` entry. Add `createPermDockUnplugin.vite()` from `permdock/unplugin` (or `.webpack` / `.esbuild`). Runtime is `permdock/h3` for Nuxt server routes, otherwise `permdock/server` or the matching HTTP adapter, plus `permdock/vue`, `permdock/react` or `permdock/svelte` on the client. Effect Schema is a Standard Schema; Effect HttpApi uses Overlay, not a hook.
491
587
 
492
588
  Remaining work follows the names on the adapter page under `/docs/adapters/<name>`. Do not invent identifiers.
@@ -4,7 +4,7 @@ Owning pages: [policies](https://permdock.com/docs/concepts/policies), [decision
4
4
 
5
5
  ## Usage limits
6
6
 
7
- A `limit: { count, per }` grant needs `limits: memoryLimitStore()` (or the app's `LimitStore`) on `createPermDock`; without it the grant denies with `limit-unavailable`. `can` never consumes a unit: a mutation that spends one calls `decide` or `assert`.
7
+ A `limit: { count, per }` grant needs `limits: limits(memoryLimitStore())` (or `limits(store)` over the app's `LimitStore`) on `createPermDock`; without it the grant denies with `limit-unavailable`. `can` never consumes a unit: a mutation that spends one calls `decide` or `assert`.
8
8
 
9
9
  - `mode: 'soft'` keeps granting past the count with an `over-limit` obligation; `alertAt: 0.8` adds a `near-limit` warning.
10
10
  - A granted decision carries `quota: { remaining, resetsAt }`. An exhausted hard limit answers `429` with `Retry-After` and `RateLimit` headers; for the header format, defer to the `ratelimit-headers` spec skill (`npx skills add ScaleDockHQ/scaledock-skills --skill ratelimit-headers`).
@@ -22,7 +22,7 @@ Localise `describe` with `messages`. `messages.reasons` is a record per denial r
22
22
 
23
23
  ## Validity windows
24
24
 
25
- Access that ends on a date (a contractor's engagement, a change freeze until launch) is `validFrom` / `validUntil` on the grant, RFC 3339 or Unix seconds. Outside the window an allow denies with `inactive-grant` and a deny does not apply; `where()` and generated RLS drop it, and `simulate(checks, { now })` previews a date. A person's access ending is the membership's `expiresAt` (see the `permdock-tenancy` skill).
25
+ Access that ends on a date (a contractor's engagement, a change freeze until launch) is `validFrom` / `validUntil` on the grant, an RFC 3339 string or an `Instant` such as a `Temporal.Instant`. Outside the window an allow denies with `inactive-grant` and a deny does not apply; `where()` and generated RLS drop it, and `simulate(checks, { now })` previews an instant (`now` is an `Instant`). A person's access ending is the membership's `expiresAt` (see the `permdock-tenancy` skill).
26
26
 
27
27
  ## Step-up
28
28
 
@@ -33,13 +33,22 @@ Hardware-key step-up is `to: assurance({ amr: ['hwk'], maxAge: 300 })` and rende
33
33
  Adopting PermDock in an app with its own keys, SQL helpers and tokens is a step-by-step guide ([existing apps](https://permdock.com/docs/getting-started/existing-apps)):
34
34
 
35
35
  - Keep the app's keys. Map verbs such as `view` to SQL with `rls.actions`, and give colliding resources a distinct `name` on `resource()`.
36
- - Put application-owned metadata in `meta.x`; PermDock carries it and never reads it.
36
+ - Put application-owned metadata in `meta.x`; PermDock carries it and never reads it (see App data below).
37
37
  - Rename a key later with `definePermissions(tree, { renamed: { "old.key": "new.key" } })`. Remove the alias only when `permdock doctor` PD055 and PD056 are quiet; `permdock diff` reports the removal as the breaking `alias-removed`.
38
- - Generate helpers beside legacy SQL with `permdock rls generate --helpers-only --shims`, then rewrite policies with `permdock rls migrate --write`.
38
+ - Generate helpers beside legacy SQL with `permdock rls generate --helpers-only --shims`, then rewrite policies with `permdock rls migrate`.
39
+
40
+ ## App data and app obligations
41
+
42
+ Owning page: [extend PermDock](https://permdock.com/docs/guides/extending).
43
+
44
+ - Type `x` with one Standard Schema per kind: `definePermissions(tree, { x: { permission, resource } })`, `defineRoles(tree, { x })`, `definePlans(tree, { x })`, `definePolicy(…, { x: { grant, membership } })`. Each schema validates synchronously. Invalid `x` in code throws; invalid `Membership.x` or custom-role `meta.x` from a source is dropped and the row stays.
45
+ - Describe a grant with `allow(p, { name, meta: { description, x } })`. It reaches `decision.matched`, decision events, snapshot grants and catalog grants, and stays out of the policy fingerprint.
46
+ - Steer code on a grant with `allow(p, { obligations: ['watermark', { name, detail }] })`, never on a deny. Read `{ kind: 'app', name, detail? }` from `decision.obligations` on a granted decision.
47
+ - Snapshots carry grant `meta`, membership `x` and every leaf's `meta` to the client: never put a secret in them.
39
48
 
40
49
  ## CLI starting points
41
50
 
42
- - When the app already has an OpenAPI document and no definitions yet, start from `permdock openapi import --doc <doc> --out src/permissions.generated.ts --schema zod` and review each action's `meta.inferredFrom`.
51
+ - When the app already has an OpenAPI document and no definitions yet, start from `permdock openapi import --doc <doc> --out src/permissions.generated.ts --validator zod` and review each action's `meta.inferredFrom`.
43
52
  - When the repo has Arazzo workflows, run `permdock arazzo check --doc <arazzo> --openapi <doc>`.
44
- - When the app uses PermDock Cloud, add `permdock cloud push` to the deploy step after the deploy, with `PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` from CI secrets. The Cloud is optional and never on the decision path.
53
+ - When the app uses PermDock Cloud, add `permdock cloud push` to the deploy step after the deploy, with `PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` from CI secrets; pass `--manifest` with the `supabase inspect --out` file when the repo has one. A repo that builds from a Cloud project runs `permdock cloud pull --project <id>` with `PERMDOCK_CLOUD_TOKEN` before its checks instead. Locally, `permdock login` replaces both keys. The Cloud is optional and never on the decision path.
45
54
  - `permdock skills install` copies these skills into the project's agent folders, writing a symlinked folder once through its target; `permdock skills install --check` in CI fails when the installed skills differ from the package version.