permdock 0.4.0 → 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 (514) hide show
  1. package/README.md +37 -35
  2. package/dist/{schema-Bol6gqXA.js → 2020-C9VN-x8c.js} +5 -130
  3. package/dist/a2a/index.d.ts +6 -6
  4. package/dist/a2a/index.js +14 -17
  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 -35
  10. package/dist/ai-sdk/index.js +37 -23
  11. package/dist/{app-data-CV8pqqMi.js → app-data-MuyzVqX8.js} +2 -1
  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/arazzo-B6lebmiQ.js +36 -0
  19. package/dist/{arazzo-C4BcskbX.js → arazzo-BWhNF-k0.js} +20 -14
  20. package/dist/args-IIolxxKA.js +197 -0
  21. package/dist/astro/index.d.ts +43 -0
  22. package/dist/astro/index.js +27 -0
  23. package/dist/audience-Dg8XsRaO.js +55 -0
  24. package/dist/auth-DRLEizbL.js +178 -0
  25. package/dist/authzen/index.d.ts +26 -6
  26. package/dist/authzen/index.js +94 -79
  27. package/dist/automation-BMZPIVUM.d.ts +114 -0
  28. package/dist/automation-DVjOckjV.js +157 -0
  29. package/dist/better-auth/index.d.ts +8 -6
  30. package/dist/better-auth/index.js +9 -8
  31. package/dist/better-supabase/index.d.ts +357 -30
  32. package/dist/better-supabase/index.js +936 -194
  33. package/dist/{bind-BzqG8SMb.d.ts → bind--9EFVYWF.d.ts} +12 -10
  34. package/dist/{bind-kk-uyCwH.js → bind-CZzhx-Vj.js} +14 -10
  35. package/dist/body-CH5of8Ne.js +10 -0
  36. package/dist/body-text-QcXok_SI.js +52 -0
  37. package/dist/{build-instance-DIIV9DbC.js → build-instance-BMnAudHn.js} +4 -3
  38. package/dist/{canonical-json-CFKyFOke.js → canonical-json-BC8_nMTw.js} +5 -3
  39. package/dist/{capability-CksyCWDH.d.ts → capability-BSslw1QV.d.ts} +44 -25
  40. package/dist/{capability-txATtKV4.js → capability-N1K9IpqS.js} +68 -42
  41. package/dist/casbin-Cr7RLtA3.js +30 -0
  42. package/dist/casbin-Dyu2gnUD.js +234 -0
  43. package/dist/casl-64gpT-8x.js +24 -0
  44. package/dist/casl-DjEDhTCP.js +299 -0
  45. package/dist/catalog/index.d.ts +74 -2
  46. package/dist/catalog/index.js +51 -2
  47. package/dist/{catalog-tZUyOfOv.js → catalog-BECMPrwc.js} +51 -6
  48. package/dist/catalog-DjhMYYCq.js +1 -0
  49. package/dist/catalog-R_A__RsH.js +52 -0
  50. package/dist/{catalog-doc-DWtg0gGR.js → catalog-doc-C9wjsfNY.js} +45 -23
  51. package/dist/catalog-fingerprint-CCP0-yQS.js +153 -0
  52. package/dist/chat/index.d.ts +2 -0
  53. package/dist/chat/index.js +224 -0
  54. package/dist/chat-sdk/index.d.ts +87 -0
  55. package/dist/chat-sdk/index.js +197 -0
  56. package/dist/claims-map-7oQ3ue4X.js +13 -0
  57. package/dist/claims-map-Ca-QX8C6.d.ts +32 -0
  58. package/dist/claude-agent/index.d.ts +7 -14
  59. package/dist/claude-agent/index.js +24 -27
  60. package/dist/clerk/index.d.ts +9 -8
  61. package/dist/clerk/index.js +9 -14
  62. package/dist/cli/bin.js +36 -17
  63. package/dist/cli/index.d.ts +19 -5
  64. package/dist/cli/index.js +164 -4
  65. package/dist/cloud/index.d.ts +102 -63
  66. package/dist/cloud/index.js +239 -340
  67. package/dist/cloud-CIoHNevm.js +87 -0
  68. package/dist/codemod-Bmj9dWMH.js +37 -0
  69. package/dist/codemod-Cxvvc70T.js +2084 -0
  70. package/dist/{collect-2SrB7lfk.js → collect-CrZJJnEF.js} +75 -11
  71. package/dist/collect-DloHbDVa.js +55 -0
  72. package/dist/collect-FlXJlhus.js +1 -0
  73. package/dist/command-L8EstFNm.js +6 -0
  74. package/dist/compile/index.d.ts +1 -1
  75. package/dist/compile/index.js +1 -2
  76. package/dist/{compile-CcEu2ZQ1.js → compile-CMqMx_IN.js} +11 -8
  77. package/dist/{compile-CtTP7eur.d.ts → compile-Ckm2HqBZ.d.ts} +5 -3
  78. package/dist/completion-BqhRBJD8.js +143 -0
  79. package/dist/config-B46rm9sc.js +26 -0
  80. package/dist/{config-BIjREpyE.js → config-bs_kZeYq.js} +12 -7
  81. package/dist/{config-report--Xb9APJS.js → config-report-DUgj62Ai.js} +15 -5
  82. package/dist/{connection-DUsczpB5.js → connection-DjhIJSs2.js} +6 -5
  83. package/dist/{context-DieZVJXu.js → context-bbFHR-0D.js} +2 -2
  84. package/dist/convex/index.d.ts +11 -10
  85. package/dist/convex/index.js +6 -4
  86. package/dist/create-0VZ7Pdiu.d.ts +107 -0
  87. package/dist/create-BDS7wc3y.d.ts +138 -0
  88. package/dist/create-BFfm17UA.d.ts +110 -0
  89. package/dist/create-BWRSXgOF.js +403 -0
  90. package/dist/create-C3b4B5eF.js +130 -0
  91. package/dist/{create-BBO24vj6.js → create-DnrmLnLW.js} +31 -32
  92. package/dist/{credential-kue0qpbm.js → credential-DM2um3ha.js} +152 -31
  93. package/dist/credentials-DPyHmYz5.js +74 -0
  94. package/dist/delegation-DaXVdDhM.js +286 -0
  95. package/dist/delivery/index.d.ts +5 -0
  96. package/dist/delivery/index.js +5 -0
  97. package/dist/delivery-DCAgNyCn.js +499 -0
  98. package/dist/delivery-event-xX4obB8F.js +40 -0
  99. package/dist/{denied-text-DfRk15uw.d.ts → denied-text-D8jDHgsl.d.ts} +5 -5
  100. package/dist/{describe-C3ldA9os.d.ts → describe-BZUsixiL.d.ts} +2 -2
  101. package/dist/{describe-Cgkp7eP0.js → describe-BybMRY7A.js} +6 -2
  102. package/dist/device-BARQ8eYG.js +193 -0
  103. package/dist/diff-CxZo9pTq.js +37 -0
  104. package/dist/{diff-rpqoe7Ag.js → diff-DCePiN6M.js} +60 -54
  105. package/dist/doctor-CxiwSfqH.js +44 -0
  106. package/dist/{doctor-owlv6ddh.js → doctor-DFjUxf2f.js} +245 -50
  107. package/dist/drizzle/index.d.ts +5 -3
  108. package/dist/drizzle/index.js +2 -2
  109. package/dist/elysia/index.d.ts +16 -13
  110. package/dist/elysia/index.js +13 -12
  111. package/dist/env-DJZrPjRi.js +8 -0
  112. package/dist/errors-BDUwg2qC.js +27 -0
  113. package/dist/{errors-drnfxYtm.js → errors-BKy0EfhN.js} +5 -9
  114. package/dist/errors-BWXBMaKK.d.ts +11 -0
  115. package/dist/{errors-OD_4c__H.d.ts → errors-C2PXIv4c.d.ts} +2 -2
  116. package/dist/errors-CWTY6uih.js +143 -0
  117. package/dist/evaluate-_R8QkWaf.js +1732 -0
  118. package/dist/evaluations-16UqqQ6W.d.ts +32 -0
  119. package/dist/{evaluations-C9YJsv3H.js → evaluations-DZ2CbV5k.js} +145 -34
  120. package/dist/eve/index.d.ts +12 -16
  121. package/dist/eve/index.js +22 -28
  122. package/dist/event-time-CsegxXxB.js +23 -0
  123. package/dist/expiry-D4_YKRKD.js +52 -0
  124. package/dist/express/index.d.ts +13 -10
  125. package/dist/express/index.js +12 -11
  126. package/dist/fastify/index.d.ts +25 -22
  127. package/dist/fastify/index.js +13 -12
  128. package/dist/fetch-adapter-g0ypLkID.js +120 -0
  129. package/dist/fetch-adapter-qKJBuRqy.d.ts +31 -0
  130. package/dist/fields-D9tPVUUS.js +203 -0
  131. package/dist/files-C5ZI27eV.js +124 -0
  132. package/dist/{fixtures-h2eG2OZV.js → fixtures-B7XrLeY7.js} +8 -7
  133. package/dist/flags-C3DktBDY.js +86 -0
  134. package/dist/{from-snapshot-7REQZe0H.js → from-snapshot-D2lZt4Dd.js} +55 -52
  135. package/dist/{grantee-CXY-0C87.js → grantee-Cn1HyUsI.js} +3 -3
  136. package/dist/{grantee-conditions-MQs5NIuX.js → grantee-conditions-CcXf8hMM.js} +4 -3
  137. package/dist/{grantee-match-fxYp9Nmt.js → grantee-match-DHArqY82.js} +5 -4
  138. package/dist/h3/index.d.ts +37 -0
  139. package/dist/h3/index.js +35 -0
  140. package/dist/headers-BIjfMaoC.js +8 -0
  141. package/dist/headers-CIbDTUTK.d.ts +7 -0
  142. package/dist/{helpers-C9YmyBIa.js → helpers-DTcZOzhF.js} +45 -38
  143. package/dist/hono/index.d.ts +12 -9
  144. package/dist/hono/index.js +13 -12
  145. package/dist/hosted-BZIAYvon.js +315 -0
  146. package/dist/hosted-DBDX0M-P.d.ts +78 -0
  147. package/dist/{http-DPuDaChb.d.ts → http-B_LphE5Y.d.ts} +1 -2
  148. package/dist/http-DXJ37RDA.js +46 -0
  149. package/dist/{http-BEbdMZ05.js → http-ujKm0JXE.js} +1 -2
  150. package/dist/{import-UHk1N7vH.js → import-CNV7I2Dw.js} +25 -20
  151. package/dist/{import-BFIlhetD.js → import-DMmOG27O.js} +190 -53
  152. package/dist/index-BcHcPyIo.d.ts +110 -0
  153. package/dist/index-CaxrMD1A.d.ts +1 -0
  154. package/dist/index-Chrs5fQ-.d.ts +210 -0
  155. package/dist/index-CxGUswIC.d.ts +431 -0
  156. package/dist/index-D8EovfsP.d.ts +91 -0
  157. package/dist/{index-UdVQgq48.d.ts → index-DOVf7RJv.d.ts} +431 -81
  158. package/dist/{index-BwMe94Xq.d.ts → index-SGdIQyzc.d.ts} +34 -16
  159. package/dist/index.d.ts +19 -15
  160. package/dist/index.js +31 -21
  161. package/dist/init-DeMYsnYJ.js +96 -0
  162. package/dist/init-GG6Jh8HD.js +95 -0
  163. package/dist/{instance-options-plkrsAog.d.ts → instance-options-Omfd793B.d.ts} +2 -2
  164. package/dist/instance-snapshot-Cxq4ygFc.js +600 -0
  165. package/dist/instant-BluqDvyS.js +19 -0
  166. package/dist/internals-Dk8A8fX3.js +17 -0
  167. package/dist/{json-schema-DBkdUuCO.js → json-schema-B-vAwlde.js} +45 -6
  168. package/dist/json-schema-CN_iLFH2.d.ts +40 -0
  169. package/dist/jwt/index.d.ts +18 -11
  170. package/dist/jwt/index.js +156 -135
  171. package/dist/{kernel-CB3LrIJM.js → kernel-5JatXyND.js} +52 -24
  172. package/dist/kysely/index.d.ts +5 -3
  173. package/dist/kysely/index.js +2 -2
  174. package/dist/load-BxEtwlBS.js +1 -0
  175. package/dist/{load-C3_oTHyO.js → load-DFS_ry0W.js} +22 -6
  176. package/dist/{load-jose-BL6LH4bL.js → load-jose-DZ4dnjri.js} +1 -4
  177. package/dist/login-Dfe5RlYk.js +68 -0
  178. package/dist/login-imhuwvn3.js +254 -0
  179. package/dist/manifest-C8IzW3Do.js +42 -0
  180. package/dist/manifest-CtKR3GXL.js +370 -0
  181. package/dist/manifest-json-CAGzls-C.js +9 -0
  182. package/dist/{manifest-uDS9y5de.d.ts → manifest-mx5ghSFM.d.ts} +9 -2
  183. package/dist/manifest-schema-C0kK_8V0.js +399 -0
  184. package/dist/{map-claims-AVlBaJoa.js → map-claims-D3oRmTk0.js} +13 -10
  185. package/dist/{map-error-BE27PYkE.js → map-error-TCAdw-eW.js} +3 -3
  186. package/dist/{markers-CiwAOZtO.js → markers-C6joDI3o.js} +2 -1
  187. package/dist/mcp/index.d.ts +22 -12
  188. package/dist/mcp/index.js +57 -39
  189. package/dist/{migrate-BwlB5-Wk.js → migrate-BShP7Vf4.js} +53 -14
  190. package/dist/nest/index.d.ts +12 -9
  191. package/dist/nest/index.js +15 -14
  192. package/dist/next/index.d.ts +18 -21
  193. package/dist/next/index.js +13 -11
  194. package/dist/next/plugin.d.ts +1 -1
  195. package/dist/next/plugin.js +2 -2
  196. package/dist/node/index.d.ts +10 -10
  197. package/dist/node/index.js +7 -9
  198. package/dist/{ocsf-uYFXGVw2.js → ocsf-BFp-PZZA.js} +22 -11
  199. package/dist/openai/index.d.ts +9 -16
  200. package/dist/openai/index.js +17 -24
  201. package/dist/openapi/index.d.ts +3 -3
  202. package/dist/openapi/index.js +1 -1
  203. package/dist/{openapi-DBzHQbov.js → openapi-B7HZ0f-r.js} +8 -8
  204. package/dist/{openapi-BJdBmYDG.js → openapi-DTaEmTSW.js} +10 -9
  205. package/dist/openapi-ahr8rHu3.js +138 -0
  206. package/dist/operation-findings-DgATDpvh.js +36 -0
  207. package/dist/operations-CxybZErY.d.ts +59 -0
  208. package/dist/orpc/index.d.ts +13 -10
  209. package/dist/orpc/index.js +20 -15
  210. package/dist/otel/index.d.ts +3 -2
  211. package/dist/otel/index.js +30 -7
  212. package/dist/output-Dyw8fyPF.js +88 -0
  213. package/dist/output-files-B3IZJksC.js +1 -0
  214. package/dist/{output-files-CgIsvdSa.js → output-files-DhRBBE4n.js} +24 -26
  215. package/dist/{permission-keys-B26jeyUv.js → ownership-BatFgAA4.js} +1698 -869
  216. package/dist/{package-root-DtNnpmUd.js → package-root-Dk_ETdkp.js} +1 -1
  217. package/dist/{parse-CVFQHk6c.js → parse-Cxq0WxrA.js} +2 -2
  218. package/dist/{parse-local-manifest-C-q02Pal.js → parse-local-manifest-CVSs6J9I.js} +1 -1
  219. package/dist/{parse-local-manifest-DvYUR0QL.d.ts → parse-local-manifest-DgqtruPn.d.ts} +1 -1
  220. package/dist/parse-request-D_tUXG6M.js +26 -0
  221. package/dist/paths-DXSHAMIa.js +42 -0
  222. package/dist/pdp/index.d.ts +7 -3
  223. package/dist/pdp/index.js +45 -26
  224. package/dist/{peer-BOr_kJE1.js → peer-C8swJ2GH.js} +23 -4
  225. package/dist/peer-CRvC7Hg1.js +1 -0
  226. package/dist/permdock-B9JxkBwC.js +868 -0
  227. package/dist/permdock-D7eeLJER.d.ts +502 -0
  228. package/dist/{permission-registry-BsblpB-z.js → permission-registry-Br4jSNVi.js} +3 -3
  229. package/dist/permissions-B-wCP5ZC.js +30 -0
  230. package/dist/{permissions-CCJ6lmxB.js → permissions-CZKiUfFM.js} +4 -3
  231. package/dist/{permissions-COH-KRgT.d.ts → permissions-oEWfl-kl.d.ts} +1 -2
  232. package/dist/policies-CO1oOymN.js +295 -0
  233. package/dist/{policy-ur0BBK_e.d.ts → policy-Czo4n3Hs.d.ts} +2 -2
  234. package/dist/{hosted-CBHgkzDl.js → policy-DLZJ5gAp.js} +17 -273
  235. package/dist/policy-from-catalog-DzhEc3Y9.js +450 -0
  236. package/dist/policy-module-CTlwMvLI.js +89 -0
  237. package/dist/{policy-types-sp9P1iIV.d.ts → policy-types-DzwivBgR.d.ts} +434 -631
  238. package/dist/postgrest-CW0IqC-U.js +566 -0
  239. package/dist/postgrest-NUqZT-RB.d.ts +100 -0
  240. package/dist/powersync-BHfuehf3.js +62 -0
  241. package/dist/powersync-CbzkqL_M.js +1 -0
  242. package/dist/{powersync-cZe4kzhL.js → powersync-bYUDLOAO.js} +23 -24
  243. package/dist/preset/index.d.ts +146 -0
  244. package/dist/preset/index.js +1 -0
  245. package/dist/preset-DZdU0tne.js +1768 -0
  246. package/dist/principal-source-pHneukrx.js +103 -0
  247. package/dist/prisma/index.d.ts +6 -3
  248. package/dist/prisma/index.js +2 -2
  249. package/dist/{problem-CnWraVhC.js → problem-CTviyH5L.js} +13 -23
  250. package/dist/{problem-details-rIIo8B2C.d.ts → problem-details-DXlt-vks.d.ts} +1 -1
  251. package/dist/{problem-details-DECUnXAn.js → problem-details-cn5X30gZ.js} +43 -40
  252. package/dist/problem-types-CJ2325SZ.js +5 -0
  253. package/dist/{prompt-CXYhLfsS.js → prompt-DfZLJLIn.js} +2 -2
  254. package/dist/{protected-B3_P7xuw.d.ts → protected-DEPHc2xV.d.ts} +16 -8
  255. package/dist/{protected-BN7BUdY1.js → protected-DfjY5mLA.js} +21 -4
  256. package/dist/{provider-Bi-0HNL3.js → provider-CC-u3bYP.js} +2 -2
  257. package/dist/{provider-kwHC9CZD.d.ts → provider-DMVt6yYn.d.ts} +1 -1
  258. package/dist/{provider-store-Ctt-81Ni.js → provider-store-B5Yrkhzr.js} +2 -2
  259. package/dist/pull-BgIfno6w.js +256 -0
  260. package/dist/pull-ByOks_Tg.js +1 -0
  261. package/dist/push-CmFVBOeI.js +116 -0
  262. package/dist/pushed-policy-BImMTTIM.d.ts +57 -0
  263. package/dist/pushed-policy-DobP7AHI.js +238 -0
  264. package/dist/react/index.d.ts +8 -8
  265. package/dist/react/index.js +6 -6
  266. package/dist/react/provider-client.d.ts +1 -1
  267. package/dist/react/provider-client.js +1 -2
  268. package/dist/react-native/index.d.ts +24 -72
  269. package/dist/react-native/index.js +24 -22
  270. package/dist/{refs-f3imMjNW.js → refs-K6eWUJ0H.js} +1 -1
  271. package/dist/{relation-walk-BXnlLtg2.js → relation-walk-3OyDBF0S.js} +2 -2
  272. package/dist/{relations-CvmSpG67.js → relations-BnCWnJGI.js} +3 -3
  273. package/dist/renames-CGj7YwcQ.js +175 -0
  274. package/dist/{replay-COpmZUfx.d.ts → replay-D7iKcLFe.d.ts} +1 -1
  275. package/dist/{replay-BsOyTrdV.js → replay-Du1tJjG7.js} +2 -1
  276. package/dist/{request-context-BVM7aWNs.js → request-context-BTRTG7OT.js} +3 -3
  277. package/dist/request-schema-CMreVuFM.js +148 -0
  278. package/dist/resend/index.d.ts +126 -0
  279. package/dist/resend/index.js +379 -0
  280. package/dist/{resource-relations-D1Un3pcz.js → resource-relations-D_jw0BFx.js} +1 -1
  281. package/dist/{retire-BFcxL6un.js → retire-CALjIQ7e.js} +2 -2
  282. package/dist/{revocations-Cy06JNx2.d.ts → revocations-R07lFBnX.d.ts} +2 -0
  283. package/dist/rls-BA8an5Ap.js +336 -0
  284. package/dist/{rls-BEktUaqg.js → rls-j6t43t7n.js} +287 -689
  285. package/dist/{role-builder-B4GbKM-i.js → role-builder-CXbis5-X.js} +14 -207
  286. package/dist/{rpc-nYC3cR_u.js → rpc-aPyNi6tA.js} +7 -7
  287. package/dist/run-CNsApDpd.js +1 -0
  288. package/dist/run-DOfMtc9V.js +723 -0
  289. package/dist/run-ticket-BjhTOi6u.d.ts +124 -0
  290. package/dist/run-ticket-sxFI6LAk.js +337 -0
  291. package/dist/{runtime-D_tMXnsq.js → runtime-D3VTAiCA.js} +1 -1
  292. package/dist/{saas-DtaA4JuW.js → saas-FO-Q8oj0.js} +16 -14
  293. package/dist/scan-BOI5tJ-6.js +29 -0
  294. package/dist/{schema-DjQBcAP9.js → schema-B4v0Qrac.js} +102 -8
  295. package/dist/schema-yLuBWWsh.js +130 -0
  296. package/dist/scim/index.d.ts +2 -2
  297. package/dist/scim/index.js +1 -2
  298. package/dist/{scim-vFR1O5oG.js → scim-ClHtpyV3.js} +208 -76
  299. package/dist/{scopes-tvpa34ep.js → scopes-CUPSjo9q.js} +12 -7
  300. package/dist/server/index.d.ts +12 -143
  301. package/dist/server/index.js +12 -9
  302. package/dist/server-DqCT4CgZ.js +453 -0
  303. package/dist/{server-types-DdqBwZN_.d.ts → server-types-BLd4MnBX.d.ts} +51 -68
  304. package/dist/{sha256-BO0swRXL.js → sha256-D9ihqmvd.js} +8 -1
  305. package/dist/{shared-1uhI-e-b.js → shared-CD11nBbC.js} +18 -8
  306. package/dist/{shared-JhMeOsDF.js → shared-DvvFzYXe.js} +8 -9
  307. package/dist/signals-D3NcTKUS.js +32 -0
  308. package/dist/sink-BEZSlMS_.js +957 -0
  309. package/dist/sink-Bj_lDUDq.d.ts +225 -0
  310. package/dist/{skills-BZdRQkxs.js → skills-B6lVdD_W.js} +13 -6
  311. package/dist/skills-CTYwkl44.js +51 -0
  312. package/dist/snapshot-CXUWfsNy.js +269 -0
  313. package/dist/{snapshot-cache-Cod40D-X.js → snapshot-cache-DZHQ0KF6.js} +11 -5
  314. package/dist/{snapshot-cache-J5T7t_vD.d.ts → snapshot-cache-DqK9eDmM.d.ts} +7 -5
  315. package/dist/solid/index.d.ts +11 -12
  316. package/dist/solid/index.js +7 -7
  317. package/dist/solid-start/index.d.ts +52 -0
  318. package/dist/solid-start/index.js +49 -0
  319. package/dist/{sources-Dljy06zL.d.ts → sources-BV9j0DeZ.d.ts} +7 -13
  320. package/dist/{sources-B1-b-R0t.js → sources-vs5dGOsS.js} +125 -119
  321. package/dist/{sql-t8teWOfo.js → sql-B5T1IL83.js} +13 -1
  322. package/dist/src-DLfUWFAI.js +1851 -0
  323. package/dist/ssf/index.d.ts +4 -4
  324. package/dist/ssf/index.js +384 -47
  325. package/dist/standard-webhooks-6lg8v_is.d.ts +88 -0
  326. package/dist/standard-webhooks-DgFFD-Ga.js +228 -0
  327. package/dist/storage-C2bWdvJH.js +132 -0
  328. package/dist/{store-ILkLUj_o.js → store-4u_hxjWa.js} +40 -53
  329. package/dist/{store-options-CbUHDoSe.js → store-options-BqEakYRa.js} +26 -21
  330. package/dist/{stores-BHXdSbw9.js → stores-BbsD4fAR.js} +2 -2
  331. package/dist/{stores-Cxz_80g-.d.ts → stores-ClZUgeoP.d.ts} +7 -8
  332. package/dist/{stream-Dwsg6WwE.d.ts → stream-7-xmnf3U.d.ts} +2 -2
  333. package/dist/{stream-BMui98KK.js → stream-CvdUS9V1.js} +9 -2
  334. package/dist/style-DiKWnAvn.js +1 -0
  335. package/dist/{style-CTm1AXDT.js → style-Uao76wO7.js} +3 -2
  336. package/dist/subject-BGoM5o8B.js +17 -0
  337. package/dist/supabase/index.d.ts +23 -106
  338. package/dist/supabase/index.js +5 -4
  339. package/dist/supabase/middleware.d.ts +7 -6
  340. package/dist/supabase/middleware.js +13 -11
  341. package/dist/supabase-9pqI57pW.js +92 -0
  342. package/dist/{supabase-C1TWlnYt.js → supabase-Dxlp8A94.js} +98 -205
  343. package/dist/supabase-hook-CXcGpHqq.js +1 -0
  344. package/dist/{supabase-hook-Cva3ikXz.js → supabase-hook-DdDQhMlw.js} +951 -60
  345. package/dist/support-session-C6xJQYut.js +158 -0
  346. package/dist/svelte/Protected.svelte +4 -4
  347. package/dist/svelte/index.d.ts +7 -7
  348. package/dist/svelte/index.js +10 -10
  349. package/dist/svelte/runtime.d.ts +3 -3
  350. package/dist/svelte/runtime.js +3 -3
  351. package/dist/svelte/source.d.ts +7 -7
  352. package/dist/svelte/source.js +4 -4
  353. package/dist/sveltekit/index.d.ts +51 -0
  354. package/dist/sveltekit/index.js +27 -0
  355. package/dist/tanstack-start/index.d.ts +51 -0
  356. package/dist/tanstack-start/index.js +40 -0
  357. package/dist/target-Bw37TFNA.js +51 -0
  358. package/dist/{tenancy-DshjqY0n.js → tenancy-DN2QAx9z.js} +5 -5
  359. package/dist/terminal/index.d.ts +11 -9
  360. package/dist/terminal/index.js +23 -287
  361. package/dist/testing/index.d.ts +311 -38
  362. package/dist/testing/index.js +3475 -201
  363. package/dist/testing/saas/index.d.ts +9 -8
  364. package/dist/testing/saas/index.js +2 -2
  365. package/dist/testing/saas/permissions.d.ts +1 -1
  366. package/dist/testing/saas/permissions.js +1 -2
  367. package/dist/text-BMFRfBqc.js +8 -0
  368. package/dist/{text-diff-BdkkYSbi.js → text-diff-BQcUapOb.js} +1 -1
  369. package/dist/thenable-yD7SabqG.js +31 -0
  370. package/dist/time-DkWjdL98.js +71 -0
  371. package/dist/{token-Bx9qy30p.js → token-BLZcjQgt.js} +15 -6
  372. package/dist/{tool-refusal-CQ7OEia9.js → tool-refusal-CMLN5xhn.js} +2 -2
  373. package/dist/trpc/index.d.ts +12 -9
  374. package/dist/trpc/index.js +16 -14
  375. package/dist/{types-7rJVLWe5.d.ts → types-5Ut_pbKy.d.ts} +61 -11
  376. package/dist/{store-9KO4BlkB.d.ts → types-BNORmDM8.d.ts} +100 -75
  377. package/dist/types-BjV-fqcA.d.ts +57 -0
  378. package/dist/{types-CUcFZfpU.d.ts → types-BqAKcRNW.d.ts} +2 -1
  379. package/dist/types-C8-KNsFf.d.ts +107 -0
  380. package/dist/{types-Br9IXUBG.d.ts → types-CCpc2Dcv.d.ts} +77 -17
  381. package/dist/{types-DpWfQdSh.d.ts → types-CZTnPFzM.d.ts} +46 -11
  382. package/dist/{types-CqrjB9o6.d.ts → types-CdlxsYQh.d.ts} +23 -11
  383. package/dist/{types-CIlTojhq.d.ts → types-ClZaWzfb.d.ts} +9 -6
  384. package/dist/types-DX0LaKTb.d.ts +157 -0
  385. package/dist/types-DiQjupG2.d.ts +332 -0
  386. package/dist/types-DqzsnlSA.js +67 -0
  387. package/dist/types-_sHy8BQf.d.ts +65 -0
  388. package/dist/{types-ByUo56un.d.ts → types-fbZ7JEXr.d.ts} +23 -22
  389. package/dist/{types-DHrmBJLH.d.ts → types-tIgIeIKJ.d.ts} +7 -7
  390. package/dist/unplugin/index.d.ts +1 -1
  391. package/dist/unplugin/index.js +2 -2
  392. package/dist/usage-CHOtwL19.js +1 -0
  393. package/dist/usage-DHtE8j4E.js +32 -0
  394. package/dist/{usage-CG9nOYZw.js → usage-T5mXq98v.js} +54 -11
  395. package/dist/{validation-error-BSSw0l_8.d.ts → validation-error-Bg5Tpluk.d.ts} +1 -1
  396. package/dist/{validity-dVrUEPJt.js → validity-CZ7-y0Cj.js} +32 -15
  397. package/dist/{verifier-DMr_Zmb_.js → verifier-DHuh-okk.js} +18 -7
  398. package/dist/{verify-BztUB3GX.js → verify-DbCL9bnB.js} +645 -50
  399. package/dist/{version-BEogC-rB.js → version-CEbQ5UeM.js} +4 -3
  400. package/dist/version-DXkYUISx.js +1 -0
  401. package/dist/{views-CJsrBS1f.js → views-DTIIO-oQ.js} +22 -5
  402. package/dist/{permission-types-Dg39z9Kv.d.ts → vocabulary-D6lPdb4Z.d.ts} +65 -2
  403. package/dist/vue/index.d.ts +9 -10
  404. package/dist/vue/index.js +8 -8
  405. package/dist/waiter-BGUtGYoU.js +67 -0
  406. package/dist/waiter-CKy4MQSY.d.ts +36 -0
  407. package/dist/watch-Cb-NcaUw.js +1 -0
  408. package/dist/{watch-zeucB7eF.js → watch-CszoTJMq.js} +44 -5
  409. package/dist/webmcp/index.d.ts +2 -2
  410. package/dist/webmcp/index.js +3 -3
  411. package/dist/where-scope-Dv-WTP-w.js +306 -0
  412. package/dist/who-can-D0QoYbEb.js +364 -0
  413. package/dist/wire-delegation-Cv3W2eaM.js +36 -0
  414. package/dist/wire-denial-C7PaVYEM.js +27 -0
  415. package/dist/workflow/index.d.ts +5 -0
  416. package/dist/workflow/index.js +223 -0
  417. package/dist/workflow-sdk/index.d.ts +56 -0
  418. package/dist/workflow-sdk/index.js +78 -0
  419. package/dist/{wrap-Cr93qqdP.d.ts → wrap-Bd1Tejur.d.ts} +2 -1
  420. package/dist/{wrap-DWsU_5Bd.js → wrap-gX61EtpB.js} +7 -6
  421. package/package.json +136 -37
  422. package/schemas/access-event-v1.json +118 -0
  423. package/schemas/approval-request-v1.json +34 -1
  424. package/schemas/approval-signature-v1.json +94 -0
  425. package/schemas/automation-event-v1.json +113 -0
  426. package/schemas/capability-v1.json +84 -0
  427. package/schemas/catalog-event-v1.json +87 -0
  428. package/schemas/catalog-v1.json +123 -12
  429. package/schemas/cli-commands-v1.json +164 -0
  430. package/schemas/cli-result-v1.json +96 -0
  431. package/schemas/cloud-api-v1.json +5407 -0
  432. package/schemas/connector-v1.json +408 -0
  433. package/schemas/credential-event-v1.json +81 -0
  434. package/schemas/credential-v1.json +134 -0
  435. package/schemas/decision-event-v1.json +288 -0
  436. package/schemas/delivery-event-v1.json +117 -0
  437. package/schemas/directory-event-v1.json +85 -0
  438. package/schemas/evidence-export-v1.json +54 -0
  439. package/schemas/membership-event-v1.json +93 -0
  440. package/schemas/policy-v1.json +68 -0
  441. package/schemas/pushed-policy-v1.json +236 -0
  442. package/schemas/run-v1.json +162 -0
  443. package/schemas/snapshot-v1.json +138 -0
  444. package/schemas/supabase-claims-v1.json +4 -4
  445. package/schemas/supabase-manifest-v1.json +5 -1
  446. package/schemas/support-session-v1.json +111 -0
  447. package/skills/permdock/SKILL.md +16 -12
  448. package/skills/permdock-agents/SKILL.md +40 -23
  449. package/skills/permdock-agents/references/operations-automations-chat.md +221 -0
  450. package/skills/permdock-agents/references/runtimes.md +22 -18
  451. package/skills/permdock-approvals/SKILL.md +29 -13
  452. package/skills/permdock-approvals/references/delivery-and-waiters.md +172 -0
  453. package/skills/permdock-approvals/references/stores-and-resume.md +21 -7
  454. package/skills/permdock-audit/SKILL.md +3 -3
  455. package/skills/permdock-credentials/SKILL.md +5 -5
  456. package/skills/permdock-credentials/references/tokens-keys-links.md +14 -10
  457. package/skills/permdock-data/SKILL.md +6 -6
  458. package/skills/permdock-data/references/queries-and-rls.md +9 -8
  459. package/skills/permdock-data/references/relationships.md +3 -3
  460. package/skills/permdock-tenancy/SKILL.md +20 -7
  461. package/skills/permdock-tenancy/references/contacts-and-support-sessions.md +122 -0
  462. package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +5 -4
  463. package/skills/permdock-wire/SKILL.md +17 -11
  464. package/skills/permdock-wire/references/adapters.md +139 -51
  465. package/skills/permdock-wire/references/policy-options.md +5 -5
  466. package/skills/permdock-wire/references/presets-and-recipes.md +58 -0
  467. package/dist/approvals-DZL76lKW.js +0 -240
  468. package/dist/arazzo-CD8d0WLb.js +0 -54
  469. package/dist/catalog-6yWAX6Ps.js +0 -2
  470. package/dist/catalog-WHG4kBq7.js +0 -48
  471. package/dist/cloud-C7bSDgX4.js +0 -50
  472. package/dist/cloud-PeBN3IEn.js +0 -118
  473. package/dist/collect-Bw2Jdq1q.js +0 -2
  474. package/dist/collect-CQx4hX4N.js +0 -83
  475. package/dist/commands-Cpip4ULx.js +0 -97
  476. package/dist/config-DLt6BvV7.js +0 -36
  477. package/dist/diff-Cnghh_Ip.js +0 -48
  478. package/dist/doctor-CuVJvhV_.js +0 -53
  479. package/dist/duration-xK2jGFVc.js +0 -25
  480. package/dist/errors-CWfoNg9N.js +0 -47
  481. package/dist/errors-DRnGZHzF.js +0 -30
  482. package/dist/expiry-BoBJ8Tvc.js +0 -7
  483. package/dist/fields-fwfXN01n.js +0 -1016
  484. package/dist/headers-BZSDieNT.js +0 -6
  485. package/dist/headers-Cf-zWvsO.d.ts +0 -6
  486. package/dist/ids-DC4V4CFu.js +0 -18
  487. package/dist/index-DgM7w96K.d.ts +0 -6
  488. package/dist/index-Dz7didpC.d.ts +0 -201
  489. package/dist/load-B_shHrmd.js +0 -2
  490. package/dist/manifest-BngWKryL.js +0 -770
  491. package/dist/may-use-BOn_dR_w.js +0 -78
  492. package/dist/openapi-DcQ1Kg40.js +0 -164
  493. package/dist/ownership-BgnbOJFT.js +0 -851
  494. package/dist/permdock-L26udHSk.js +0 -3646
  495. package/dist/powersync-BFSbSElQ.js +0 -62
  496. package/dist/powersync-DCDgZgfZ.js +0 -2
  497. package/dist/rls-55DQTuf5.js +0 -250
  498. package/dist/run-DMcNW_8T.js +0 -174
  499. package/dist/server-ZYEHtNHS.js +0 -268
  500. package/dist/sink-BTtmfPnE.js +0 -141
  501. package/dist/sink-CPN4zLfs.d.ts +0 -104
  502. package/dist/skills-Dle4ZoLF.js +0 -56
  503. package/dist/snapshot-Dc9CHPME.js +0 -1078
  504. package/dist/src-Bb7no-hS.js +0 -639
  505. package/dist/subject-C6dLxqHU.js +0 -24
  506. package/dist/supabase-Dbqr141s.js +0 -83
  507. package/dist/supabase-hook-BULg7Ut_.js +0 -2
  508. package/dist/thenable-QSmvt-vZ.js +0 -65
  509. package/dist/types-C0dee0Qf.d.ts +0 -197
  510. package/dist/types-DvupZE0w.d.ts +0 -9
  511. package/dist/usage-4G6t643F.js +0 -42
  512. package/dist/usage-iFLJ0-rr.js +0 -2
  513. package/dist/vocabulary-hhEFLFoL.d.ts +0 -65
  514. package/dist/watch-C8TpsAl4.js +0 -2
@@ -1,10 +1,11 @@
1
1
  # Agent runtimes
2
2
 
3
- Every agent adapter exports `createPermDock` from `permdock/<runtime>` and takes `subject`, `actor`, `delegation`, `tools` and `store`. One permission per tool. Instance actions load the row through `data`; tool input arrives as `unknown`, so parse it before loading. The current adapter page is `https://permdock.com/docs/adapters/<name>`.
3
+ Every agent adapter exports `createPermDock` from `permdock/<runtime>` and takes `subject`, `actor`, `delegation`, `tenant`, `operations`, `models`, `store`, `unmapped`, `context`, `otel`, `wrap` and `deniedText`. `ai-sdk`, `openai`, `claude-agent` and `eve` build on the `permdock/agent` contract, so each also returns `decideOperation`, `allowedOperations`, `decideModel`, `permittedModels`, `filterResults` and `gatewayOptions` ([operations-automations-chat.md](operations-automations-chat.md)). `operations` is the map `defineOperations(permissions, …)` returns, one permission per tool name. `unmapped: 'allow'` leaves a tool `operations` does not name to the runtime; the default denies it. Instance actions load the row through `data`; tool input arrives as `unknown`, so parse it before loading. The current adapter page is `https://permdock.com/docs/adapters/<name>`.
4
4
 
5
5
  ## AI SDK — `permdock/ai-sdk`
6
6
 
7
7
  ```ts
8
+ import { defineOperations } from "permdock";
8
9
  import { createPermDock } from "permdock/ai-sdk";
9
10
  import { z } from "zod";
10
11
 
@@ -18,12 +19,12 @@ export const { toolApproval, capabilityMiddleware, needsApproval } =
18
19
  kind: "ai-sdk",
19
20
  }),
20
21
  delegation: () => ({ scopes: [permissions.post.delete.scope] }),
21
- tools: {
22
+ operations: defineOperations(permissions, {
22
23
  delete_post: {
23
24
  permission: permissions.post.delete,
24
25
  data: (args) => loadPost(PostArgs.parse(args).id),
25
26
  },
26
- },
27
+ }),
27
28
  });
28
29
  ```
29
30
 
@@ -32,18 +33,19 @@ Pass `toolApproval` into `generateText` / `ToolLoopAgent`. Wrap the model with `
32
33
  ## Claude Agent SDK — `permdock/claude-agent`
33
34
 
34
35
  ```ts
36
+ import { defineOperations } from "permdock";
35
37
  import { createPermDock } from "permdock/claude-agent";
36
38
 
37
39
  export const { canUseTool, permissionRequestHook } = createPermDock(policy, {
38
40
  subject: () => user,
39
41
  actor: () => ({ id: "claude", kind: "claude-agent" }),
40
42
  delegation: () => ({ scopes: [permissions.post.delete.scope] }),
41
- tools: {
43
+ operations: defineOperations(permissions, {
42
44
  delete_post: {
43
45
  permission: permissions.post.delete,
44
46
  data: (args) => loadPost(args),
45
47
  },
46
- },
48
+ }),
47
49
  });
48
50
  ```
49
51
 
@@ -52,37 +54,39 @@ export const { canUseTool, permissionRequestHook } = createPermDock(policy, {
52
54
  ## Eve — `permdock/eve`
53
55
 
54
56
  ```ts
57
+ import { defineOperations } from "permdock";
55
58
  import { createPermDock } from "permdock/eve";
56
59
 
57
- export const { approval, approvalFor, permdock } = createPermDock(policy, {
60
+ export const { approval, approvalFor, getPermDock } = createPermDock(policy, {
58
61
  delegation: () => ({ scopes: [permissions.post.delete.scope] }),
59
- tools: {
62
+ operations: defineOperations(permissions, {
60
63
  delete_post: {
61
64
  permission: permissions.post.delete,
62
65
  data: (args) => loadPost(args),
63
66
  },
64
- },
67
+ }),
65
68
  });
66
69
  ```
67
70
 
68
- Pass `approval` as the tool's `approval`. Default subject and actor read `session.auth.initiator` / `current`. `approval.request(ctx)` maps granted to Eve's `not-applicable` (continue), approval-required to `user-approval`, denied to `{ type: 'denied', reason }`; Eve's re-check of a call nobody approved is denied. `approval.response(ctx)` maps the responder through the same `subject`. Use a durable `store` when replicas share sessions ([Eve adapter](https://permdock.com/docs/adapters/eve)).
71
+ Pass `approval` as the tool's `approval`. Default subject and actor read `session.auth.initiator` / `current`. `approval.request(ctx)` maps granted to Eve's `not-applicable` (continue), approval-required to `user-approval`, denied to `{ type: 'denied', reason }`; Eve's re-check of a call nobody approved is denied. `approval.response(ctx)` maps the responder through the same `subject`. Use a durable `store` when replicas share sessions ([Eve adapter](https://permdock.com/docs/adapters/eve)). With PermDock Cloud, pass `policies: hostedPolicies(cloud().policies)` and give `cloud()` a `verifier` for the environment JWK Set; hosted grants apply only to `hostable` permissions, so mark the ones the dashboard may extend.
69
72
 
70
73
  ## OpenAI Agents SDK — `permdock/openai`
71
74
 
72
75
  ```ts
76
+ import { defineOperations } from "permdock";
73
77
  import { createPermDock } from "permdock/openai";
74
78
 
75
- export const { needsApproval, guardTools, resolveInterruptions, permdock } =
79
+ export const { needsApproval, guardTools, resolveInterruptions, getPermDock } =
76
80
  createPermDock(policy, {
77
81
  subject: (ctx) => ctx.user,
78
82
  actor: (ctx) => ({ id: ctx.agentId, kind: "openai" }),
79
83
  delegation: (ctx) => ({ scopes: ctx.scopes }),
80
- tools: {
84
+ operations: defineOperations(permissions, {
81
85
  delete_post: {
82
86
  permission: permissions.post.delete,
83
87
  data: (args) => loadPost(args),
84
88
  },
85
- },
89
+ }),
86
90
  });
87
91
  ```
88
92
 
@@ -115,7 +119,7 @@ server.registerTool(
115
119
 
116
120
  `actor.kind` is `'mcp-client'` unless `actorKind` sets another (`'oauth-client'` makes the token the same actor kind `permdock/supabase` and `permdock/jwt` give it, so one delegation covers both), and `delegation` comes from the token's scopes and `authorization_details`. Every `registerTool` / `registerResource` / `registerPrompt` needs a `permission`. Lists are filtered per caller. A missing scope is an `insufficient_scope` step-up (HTTP `403`) naming only the scope the call needs. When the authorization server issues coarse scopes (`mcp:read`), declare them in `definePolicy({ oauthScopes })` instead of expanding scopes yourself: they are expanded into the delegation and named in challenges. Set `resource` to the server's URL so tokens for another audience are refused. Denied calls return `isError: true` with Decision `structuredContent`. `approval-required` parks the call in `store` and returns `isError: true` with the token, or an `input_required` URL request when `approval.at` is set and the client declares URL elicitation; the retried call runs once after approval (token optional under `_meta["dev.permdock/approval"]`, never from tool arguments) ([MCP adapter](https://permdock.com/docs/adapters/mcp)).
117
121
 
118
- An MCP server that is not an SDK `McpServer` cannot be wrapped: narrow the tool's `meta` with `isPermission`, filter its tool list with `mayUse(permdock, permission)` from `permdock`, and decide each call with `permdock.decide(permission, args)` in its `authorize` hook. `mayUse` is a listing hint, never a decision.
122
+ An MCP server that is not an SDK `McpServer` cannot be wrapped: narrow the tool's `meta` with `isPermission`, filter its tool list with `permdock.mayUse(permission)`, and decide each call with `permdock.decide(permission, args)` in its `authorize` hook. `mayUse` is a listing hint, never a decision.
119
123
 
120
124
  When a tool's handler calls an oRPC procedure that runs `protect`, use `protectServer(server, { enforce: 'procedure', permissionFor: (name) => permissionOf(procedures.get(name)) })` with `permissionOf` from `permdock/orpc`. `tools/list` and annotations follow the permission; the call is not guarded, so each call decides once, in the procedure. `data` and `longRunning` throw in this mode, and the mode covers the whole server. When the same operations are REST routes, declare each permission once with `operationPermissions({ 'GET /customers/{id}': { permission, operationId } })` from `permdock/openapi` and pass `permissionFor: operations.forOperation`; the HTTP gate reads `operations.forRequest(method, path)` from the same object. When two operations share a permission but need different coarse scopes (reading an export with `mcp:read`, creating one with `mcp:write`), list the permission under both in `oauthScopes`, then narrow each operation with `oauthScopes: ['mcp:read']` on its entry (or on the tool config): pass `oauthScopesFor: operations.oauthScopesForOperation` to `protectServer`, and `operations` to `permdock/server` (or `protect`'s `oauthScopes`) for the REST routes, which then answer `insufficient_scope` naming that scope. A route that checks no permission but should still require a scope (a chat endpoint, setting the active organization) uses `protect(null, undefined, { oauthScopes: ['api:chat'] })` or an operation entry with only `oauthScopes`; never write an app-side rule that maps HTTP methods to scopes.
121
125
 
@@ -160,8 +164,8 @@ Identity comes from transport auth, never the task body. `actor.kind` is `'a2a'`
160
164
  Client entry. No factory. Register snapshot-allowed tools on `document.modelContext`:
161
165
 
162
166
  ```ts
163
- import { registerTools } from "permdock/webmcp";
164
167
  import { approvalHeaders, usePermDock } from "permdock/react";
168
+ import { registerTools } from "permdock/webmcp";
165
169
 
166
170
  const permdock = usePermDock();
167
171
  const controller = new AbortController();
@@ -183,14 +187,14 @@ Never import a policy into this entry. A missing `document.modelContext` is a no
183
187
  import {
184
188
  createPermDock,
185
189
  discoverViaSignatureAgent,
186
- verifyWebBotAuth,
190
+ webBotAuthVerifier,
187
191
  } from "permdock/hono";
188
192
 
189
193
  const keys = discoverViaSignatureAgent({ allow: ["agents.example.com"] });
190
194
 
191
- export const { permdock, protect } = createPermDock(policy, {
195
+ export const { middleware, protect } = createPermDock(policy, {
192
196
  subject: (c) => c.get("user"),
193
- webBotAuth: (request) => verifyWebBotAuth(request, { verify: true, keys }),
197
+ webBotAuth: webBotAuthVerifier({ verify: true, keys }),
194
198
  });
195
199
  ```
196
200
 
@@ -201,7 +205,7 @@ A verified RFC 9421 signer becomes the actor with `kind: 'web-bot-auth'`; `deleg
201
205
  ```ts
202
206
  import { createPermDock } from "permdock/terminal";
203
207
 
204
- export const { permdock, protect, filterCommands, format, exitCode } =
208
+ export const { getPermDock, protect, filterCommands, format, exitCode } =
205
209
  createPermDock(policy, {
206
210
  subject: async ({ token }) => {
207
211
  const jwt = await token(["env", "keychain", "ci-oidc", "device"]);
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: permdock-approvals
3
- description: Requires human approval on PermDock grants and resumes the approved call. Use when adding approval to a destructive or money-moving action (delete, publish, pay, refund, approve, transfer), choosing approvers with approval by, user(), relation(), distinct, quorum, ttl, escalation or staleOn, ordering approvers with mode and stages, loading approval rules as data with an ApprovalPolicySource, separating preparer and approver with exclusiveWith, mounting approvalsHandler with an ApprovalStore, resuming with the PermDock-Approval header, mapping approval-required to an agent runtime, MCP elicitation or AG-UI interrupt, or fixing doctor PD017, PD018 or PD024.
3
+ description: Requires human approval on PermDock grants, notifies approvers and resumes the approved call. Use when adding approval to a destructive or money-moving action (delete, publish, pay, refund, transfer), choosing approvers with approval by, user(), relation(), distinct, quorum, ttl, escalation or staleOn, ordering approvers with mode and stages, covering an approver with an ApprovalDelegation, loading approval rules as data with an ApprovalPolicySource, separating preparer and approver with exclusiveWith, labelling operations with risk and effects meta, mounting approvalsHandler with an ApprovalStore, sending approval emails (approvalEmail) or chat cards (approvalCard, approvalActions) from approvalPrompt, parking a background run on an ApprovalWaiter (pollingApprovalWaiter, workflowSdkWaiter), resuming with the PermDock-Approval header, mapping approval-required to an agent runtime, MCP elicitation or AG-UI interrupt, or fixing doctor PD017, PD018 or PD024.
4
4
  license: MIT
5
5
  metadata:
6
6
  author: ScaleDockHQ
@@ -20,17 +20,21 @@ metadata:
20
20
  - Whether the approver judges the row's content (an amount, a text), so a changed row needs a new approval.
21
21
  - Whether a user confirming their own agent's call is the intent anywhere.
22
22
  - Where pending requests live: one process (`memoryApprovalStore`), the app's database, or PermDock Cloud.
23
- - Which runtimes ask: HTTP, AI SDK, Claude Agent SDK, Eve, OpenAI Agents SDK, MCP, A2A, WebMCP, a chat UI.
23
+ - Which runtimes ask: HTTP, AI SDK, Claude Agent SDK, Eve, OpenAI Agents SDK, MCP, A2A, WebMCP, a chat UI, or a background run (workflow, queue) that must wait.
24
+ - How approvers hear about a request (email, chat, the app's inbox only) and where the app's approval page lives.
24
25
 
25
26
  ## Invariants
26
27
 
27
- 1. The requester never approves by default. The actor is always refused. The principal is refused unless the grant sets `distinct: false`, and `requireDistinctApprover` on the handler refuses it even then.
28
+ 1. The requester never approves by default. The actor is always refused. The principal is refused unless the grant sets `distinct: false`, and `requireDistinctApprover` on the handler refuses it even then. A delegate is held to the same rule, and so is the delegator. One hop, and `until` is required.
28
29
  2. The approver comes from authentication on the approval surface, never from the request body, a tool argument or a link.
29
30
  3. One approval resumes one call. The token binds permission, resource, subject and actor, the resume re-runs `decide`, and `consume` spends the approval once.
30
31
  4. `can` returns `false` for `approval-required`. Only `decide`, `assert` and the adapters see the third outcome.
31
32
  5. Quorum, ttl and escalation live on the grant, not in the store. A grant's `ttl` can shorten the store's window, never extend it.
32
33
  6. A `relation()` approver is matched only through facts the approvals handler reads from the app's `RelationSource` at verdict time, never from a request body.
33
34
  7. Approval rules kept as data only add stages. A source that fails to load denies every call an allow would grant.
35
+ 8. A link never approves. An email or chat prompt carries the request id, never the token, and links to the app's approval page where the approver signs in. Chat buttons carry the request id and are applied as the platform-verified clicking user.
36
+ 9. A wake carries no verdict. `waiter.wake(token)` only ends a wait; the run decides again through `resumeApproval`, which reads the store.
37
+ 10. PermDock never decides on operation `meta`. `risk` and `effects` label or gate a call in the host; only the grant's `approval` asks a person.
34
38
 
35
39
  ## Workflow
36
40
 
@@ -41,8 +45,8 @@ metadata:
41
45
  approval: {
42
46
  by: roles.finance,
43
47
  quorum: 2,
44
- ttl: "30m",
45
- escalation: { after: "4h", to: roles.owner },
48
+ ttl: 1800, // seconds: 30 minutes
49
+ escalation: { after: 14_400, to: roles.owner }, // 4 hours
46
50
  },
47
51
  });
48
52
  ```
@@ -55,14 +59,20 @@ metadata:
55
59
  ✓ `definePolicy` accepts the grant (it throws when the resource has no `version` or the action is a collection action).
56
60
  3. **Split workflow verbs.** Model `prepare`, `approve`, `pay`, `settle` and `submit` as separate leaves. Put the approval on `approve` / `pay`, and use `exclusiveWith` on `role()` so a preparer cannot also hold the approver role.
57
61
  ✓ `permdock doctor` reports no PD018 (exclusive roles held together).
58
- 4. **Store and handler.** Create one `ApprovalStore`, pass it as `store` to every adapter, and mount `approvalsHandler(store, { subject })` for approvers; add `relations` and `permissions` when a grant uses `relation()` approvers. -> [references/stores-and-resume.md](references/stores-and-resume.md)
59
- ✓ A second authenticated user can approve; the actor and the requester get `403`.
60
- 5. **Rules as data (optional).** Pass `approvalPolicies` (an `ApprovalPolicySource`, `memoryApprovalPolicies` in process) to `createPermDock` for approval rules organisations configure. -> [references/stores-and-resume.md](references/stores-and-resume.md#approval-policies-as-data)
62
+ 4. **Label risk.** Give each approval-gated operation in `defineOperations` a `meta` with `title`, `risk` (`low`, `medium`, `high`, `critical`) and `effects` (`read`, `write`, `delete`, `send`, `spend`, `external`), so prompts and the host's UI show what the call does ([operations](https://permdock.com/docs/concepts/operations)). -> [references/delivery-and-waiters.md](references/delivery-and-waiters.md#risk-meta)
63
+ ✓ `defineOperations` builds the map without throwing, and every `risk: 'high'` or `'critical'` operation's permission has an `approval` grant.
64
+ 5. **Store and handler.** Create one `ApprovalStore`, pass it as `store` to every adapter, and mount `approvalsHandler(store, { subject })` for approvers (`GET /:id`, `POST /:id/approve` and `/reject` with `If-Match`); add `relations` and `permissions` when a grant uses `relation()` approvers. -> [references/stores-and-resume.md](references/stores-and-resume.md)
65
+ ✓ A second authenticated user can approve; the actor and the requester get `403`, and a stale `If-Match` gets `412`.
66
+ 6. **Rules as data (optional).** Pass `approvalPolicies` (an `ApprovalPolicySource`, `memoryApprovalPolicySource` in process) to `createPermDock` for approval rules organisations configure. -> [references/stores-and-resume.md](references/stores-and-resume.md#approval-policies-as-data)
61
67
  ✓ `testApprovalPolicySource` from `permdock/testing` passes on the source.
62
- 6. **Surface and resume.** Map the outcome to each runtime's approval hook and resume with the token: the `PermDock-Approval` header over HTTP, the runtime's own resume for agents. -> [references/stores-and-resume.md](references/stores-and-resume.md#surfaces)
68
+ 7. **Notify approvers.** Build the prompt with `approvalPrompt(request, { operation })` and send it with `createPermDock` from `permdock/delivery`: `approvalEmail` from `permdock/resend` for email, a chat message with `approval` (rendered as `approvalCard`) plus `approvalActions` from `permdock/chat-sdk` for chat ([delivery](https://permdock.com/docs/concepts/delivery)). -> [references/delivery-and-waiters.md](references/delivery-and-waiters.md#approval-prompts)
69
+ ✓ The `DeliveryReport` shows `sent` for each eligible approver and none for the requester; the message holds no token and no approve link.
70
+ 8. **Surface and resume.** Map the outcome to each runtime's approval hook and resume with the token: the `PermDock-Approval` header over HTTP, the runtime's own resume for agents. -> [references/stores-and-resume.md](references/stores-and-resume.md#surfaces)
63
71
  ✓ The approved call runs once; a second resume is denied with `approval-consumed`.
64
- 7. **Test.** Add a scenario test per approval grant: the ask, an approval by an eligible user, a refused self-approval, and the single resume ([scenario testing](https://permdock.com/docs/guides/scenario-testing)). Run `testApprovalStore` on a custom store.
65
- ✓ The tests pass.
72
+ 9. **Wait in background runs.** Pass a `waiter` to `createPermDock` from `permdock/workflow` (`pollingApprovalWaiter({ store, interval })`, or `workflowSdkWaiter` from `permdock/workflow-sdk`) and call `awaitApproval`, or `openApproval` and `resumeApproval` as steps. The approver's endpoint calls `waiter.wake(token)` after `resolveApproval` ([automations](https://permdock.com/docs/concepts/automations)). -> [references/delivery-and-waiters.md](references/delivery-and-waiters.md#waiters)
73
+ ✓ A wake before any verdict leaves the run denied with reason `approval`; a wake after approval lets it proceed once.
74
+ 10. **Test.** Add a scenario test per approval grant: the ask, an approval by an eligible user, a refused self-approval, and the single resume ([scenario testing](https://permdock.com/docs/guides/scenario-testing)). Run `testApprovalStore` on a custom store.
75
+ ✓ The tests pass.
66
76
 
67
77
  ## Verify before done
68
78
 
@@ -73,8 +83,14 @@ metadata:
73
83
  - [ ] No resume token is read from model output or tool arguments, and each resume re-runs `decide`.
74
84
  - [ ] A custom `ApprovalStore` passes `testApprovalStore` from `permdock/testing`.
75
85
  - [ ] Grants with `relation()` approvers have a handler with `relations`, and nothing fills `verdict.relations` from client input.
86
+ - [ ] No email, chat message or URL carries the approval token or an approve link; `approvalUrl` is https and opens a page behind sign-in.
87
+ - [ ] Approval recipients are decided by `deliver` from server-loaded principals, never from the request body, and the requester is not among them.
88
+ - [ ] Chat approve and reject clicks go through `approvalActions` (or `resolveAction`), never a handler that trusts the button value alone.
89
+ - [ ] Every approver endpoint that resolves a request for a parked run calls `waiter.wake(token)` after `resolveApproval`, and no run reads a verdict from a wake, hook payload or queue message.
90
+ - [ ] No code grants, skips or auto-approves a call because of `meta.risk` or `meta.effects`.
76
91
 
77
92
  ## Reference index
78
93
 
79
- - [references/stores-and-resume.md](references/stores-and-resume.md): `approvalsHandler` routes, custom stores, relation approvers, approval policies as data, the token, resume details and denial causes, the runtime surfaces table, delivery to chat and email.
80
- - Docs: [approval security](https://permdock.com/docs/security/approvals), [approvals adapter](https://permdock.com/docs/adapters/approvals), [wire formats](https://permdock.com/docs/concepts/wire-formats).
94
+ - [references/stores-and-resume.md](references/stores-and-resume.md): `approvalsHandler` routes and preconditions, custom stores, relation approvers, approval policies as data, the token and its CloudEvents, resume details and denial causes, the runtime surfaces table.
95
+ - [references/delivery-and-waiters.md](references/delivery-and-waiters.md): operation risk meta, `approvalPrompt` with `permdock/delivery`, `approvalEmail`, `approvalCard` and `approvalActions`, `ApprovalWaiter`, `pollingApprovalWaiter`, `workflowSdkWaiter`, `cloudApprovalWaiter`.
96
+ - Docs: [approval security](https://permdock.com/docs/security/approvals), [approvals adapter](https://permdock.com/docs/adapters/approvals), [delivery](https://permdock.com/docs/concepts/delivery), [Resend](https://permdock.com/docs/adapters/resend), [Chat SDK](https://permdock.com/docs/adapters/chat-sdk), [automations](https://permdock.com/docs/concepts/automations), [Workflow SDK](https://permdock.com/docs/adapters/workflow-sdk), [operations](https://permdock.com/docs/concepts/operations), [wire formats](https://permdock.com/docs/concepts/wire-formats).
@@ -0,0 +1,172 @@
1
+ # Risk meta, approval prompts and waiters
2
+
3
+ A pending request needs three things around it: a label saying how risky the call is, a prompt that reaches the approvers, and, for a background run, a way to park until someone answers. None of them decides anything: the verdict comes from `approvalsHandler` (or `resolveApproval`) and the resume re-runs `decide`.
4
+
5
+ ## Risk meta
6
+
7
+ `defineOperations` from `permdock` binds each action to one permission leaf and carries `meta` `{ risk?, effects?, title? }` ([operations](https://permdock.com/docs/concepts/operations)):
8
+
9
+ ```ts
10
+ import { defineOperations } from "permdock";
11
+
12
+ export const operations = defineOperations(permissions, {
13
+ "invoice.pay": {
14
+ permission: permissions.invoice.pay,
15
+ data: (input: { id: string }) => invoices.get(input.id),
16
+ meta: {
17
+ title: "Pay an invoice",
18
+ risk: "high",
19
+ effects: ["spend", "external"],
20
+ },
21
+ },
22
+ });
23
+ ```
24
+
25
+ - `risk` is one of `low`, `medium`, `high`, `critical`. `effects` lists distinct values of `read`, `write`, `delete`, `send`, `spend`, `external`. Any other value throws when the map is built.
26
+ - PermDock never decides on `meta`. The approval comes from the grant's `approval`; `meta` is what a host reads to label, confirm or gate a call (a red badge, a confirmation dialog, a rule in your own UI).
27
+ - Do not use `risk: 'high'` instead of `approval` on the grant. A label without a grant asks nobody.
28
+ - `approvalPrompt(request, { operation })` copies the operation's `name`, `title`, `risk` and `effects` into the prompt, so emails and cards show them.
29
+
30
+ ## Approval prompts
31
+
32
+ `permdock/delivery` sends a message only to recipients the policy grants, and `approvalPrompt` turns a pending request into what an approver sees ([delivery](https://permdock.com/docs/concepts/delivery)).
33
+
34
+ ```ts
35
+ import { whoCan } from "permdock";
36
+ import { requestApproval } from "permdock/approvals";
37
+ import { approvalPrompt, audience, createPermDock } from "permdock/delivery";
38
+ import { approvalEmail, resendTransport } from "permdock/resend";
39
+
40
+ const delivery = createPermDock(policy, {
41
+ transports: {
42
+ email: resendTransport(resend, { from: "Acme <billing@acme.test>" }),
43
+ },
44
+ principals, // PrincipalSource: links addresses to principals
45
+ sink,
46
+ });
47
+
48
+ const request = await requestApproval(store, decision, {
49
+ permission: permissions.invoice.pay,
50
+ resource: { type: "invoice", id: invoice.id },
51
+ subject: permdock.subject,
52
+ detail: `Pay invoice ${invoice.id}`,
53
+ });
54
+ const holders = await whoCan(permdock, permissions.invoice.pay, invoice);
55
+ const approvers = await audience(permdock, permissions.invoice.read, invoice, {
56
+ candidates: holders.holders
57
+ .map((holder) => holder.principal.id)
58
+ .filter((id) => id !== permdock.subject.principal?.id),
59
+ });
60
+ const prompt = approvalPrompt(request, {
61
+ operation: operations["invoice.pay"],
62
+ });
63
+ await delivery.deliver(
64
+ permdock,
65
+ permissions.invoice.read, // a permission every approver holds on the row
66
+ invoice,
67
+ approvers.principals.map((id) => ({
68
+ principal: id,
69
+ channel: "email",
70
+ address: emailOf(id),
71
+ })),
72
+ {
73
+ email: approvalEmail(prompt, {
74
+ approvalUrl: "https://app.acme.test/approvals",
75
+ }),
76
+ },
77
+ { approval: prompt.id },
78
+ );
79
+ ```
80
+
81
+ - `approvalPrompt` throws a `TypeError` for a request that is not pending. The prompt (`ApprovalPrompt` v1) carries the request `id`, permission, scope, resource, requester, tenant, detail, `createdAt`, `expiresAt` and the operation; it never carries the approval token, roles or memberships.
82
+ - `approvalUrl` is an https base the encoded request id is appended to, or a function of the prompt. Anything but https throws. The page it opens is the app's own approval screen, behind sign-in, that calls `approvalsHandler` (`GET /:id`, then `POST /:id/approve` or `/reject`).
83
+ - `deliver` decides every recipient with `decideFor` and never rejects: a refused, unlinked or malformed recipient is a `failed` delivery event. Pass `{ approval: prompt.id }` so every event names the request.
84
+ - Recipients come from the server: a `principal` id with the address the server loaded for it, or an address that `principals.principalByIdentity` links. Never take recipients from the request body.
85
+
86
+ ### Email
87
+
88
+ `approvalEmail(prompt, { approvalUrl, product? })` from `permdock/resend` returns an `EmailMessage` with category `approval`. The subject is `Approval needed: <title>`; the body names the requester, the permission and resource, the risk and the expiry, and has one link to the approval page. It has no approve or reject link. Send it with `resendTransport`, or with your own `EmailTransport` for another provider. `resendWebhook` turns Resend bounces and complaints into delivery events ([Resend adapter](https://permdock.com/docs/adapters/resend)).
89
+
90
+ ### Chat
91
+
92
+ `permdock/chat-sdk` renders and applies approval cards on the Chat SDK ([Chat SDK adapter](https://permdock.com/docs/adapters/chat-sdk)):
93
+
94
+ ```ts
95
+ import { createPermDock } from "permdock/chat";
96
+ import { approvalActions, chatSdkTransport } from "permdock/chat-sdk";
97
+
98
+ const chatPermDock = createPermDock(policy, {
99
+ principals,
100
+ memberships,
101
+ store: store,
102
+ });
103
+ const actions = approvalActions(chatPermDock, { workspace: "T0ACME" });
104
+ chat.onAction([...actions.actionIds], actions.onAction);
105
+
106
+ // a chat message with `approval` is posted as `approvalCard`
107
+ await delivery.deliver(
108
+ permdock,
109
+ permissions.invoice.read,
110
+ invoice,
111
+ recipients,
112
+ {
113
+ chat: {
114
+ text: prompt.detail,
115
+ approval: { prompt, url: "https://app.acme.test/approvals" },
116
+ },
117
+ },
118
+ { approval: prompt.id },
119
+ );
120
+ ```
121
+
122
+ - `approvalCard(prompt, { approvalUrl, buttons? })` shows the detail, requester, permission, resource, risk and expiry, a Review link, and Approve and Reject buttons whose `value` is the request id, never the token. `buttons: false` leaves only the link.
123
+ - `approvalActions` resolves the clicking user from the platform's signed event through `ChatPermDock.resolveAction`. A click from an unlinked user, the requester or a bot records nothing.
124
+
125
+ ### Rules
126
+
127
+ - A link never approves. A forwarded email, a link scanner or a preview bot can open it; the approval page asks the approver to sign in and decides as that session's subject.
128
+ - Buttons are allowed in chat only because the platform signs the click and names the user. Never put approve or reject links, or the token, in an email.
129
+
130
+ ## Waiters
131
+
132
+ A background run (a workflow, a queue consumer) cannot hold an HTTP request open while it waits. `createPermDock` from `permdock/workflow` takes a `waiter` (`ApprovalWaiter`) next to `approvals` ([automations](https://permdock.com/docs/concepts/automations)):
133
+
134
+ ```ts
135
+ import { createPermDock, pollingApprovalWaiter } from "permdock/workflow";
136
+
137
+ const workflow = createPermDock(policy, {
138
+ signer,
139
+ verifier,
140
+ principals,
141
+ automations,
142
+ store: store,
143
+ waiter: pollingApprovalWaiter({ store, interval: 5_000 }),
144
+ sink,
145
+ });
146
+
147
+ const decision = await workflow.awaitApproval(
148
+ ticket,
149
+ permissions.invoice.send,
150
+ invoice,
151
+ );
152
+ ```
153
+
154
+ - `ApprovalWaiter` is `{ wait(token, { expiresAt?, signal? }), wake(token) }`. `wait` resolves when the request leaves `pending`, is woken or expires; it never says how it was decided.
155
+ - `awaitApproval(ticket, permission, row)` opens the request, waits, rebuilds the run and decides again. Runners that wait outside the call use the halves: `openApproval` before the wait and `resumeApproval(ticket, permission, token, row)` after it. A request still pending denies with reason `approval`.
156
+ - `pollingApprovalWaiter({ store, interval })` reads the store every `interval` milliseconds (default 5,000); `wake` ends a wait in the same process at once.
157
+ - `workflowSdkWaiter({ createHook, resumeHook, sleep? })` from `permdock/workflow-sdk` parks a Workflow SDK run on the hook `approvalHookToken(token)`. Call `wait` inside the `"use workflow"` function and `openApproval` / `resumeApproval` as steps ([Workflow SDK adapter](https://permdock.com/docs/adapters/workflow-sdk)).
158
+ - `cloudApprovalWaiter` from `permdock/cloud` wakes waits from PermDock Cloud's `com.permdock.approval.resolved`, `.expired` and `.cancelled` webhooks ([Cloud adapter](https://permdock.com/docs/adapters/cloud)).
159
+
160
+ The approver's endpoint wakes the run after it records the verdict:
161
+
162
+ ```ts
163
+ import { resolveApproval } from "permdock/approvals";
164
+
165
+ await resolveApproval(store, token, {
166
+ status: "approved",
167
+ by: approverFromSession(request),
168
+ });
169
+ await waiter.wake(token);
170
+ ```
171
+
172
+ A wake carries no verdict. Anyone holding a Workflow SDK hook token can resume it, so the step after the wait reads the store through `resumeApproval`; a forged wake finds the request still pending and denies. Never pass the verdict through the hook payload or the queue message.
@@ -7,12 +7,12 @@ The API and the store contract are on the [approvals adapter](https://permdock.c
7
7
  ```ts
8
8
  import { approvalsHandler, memoryApprovalStore } from "permdock/approvals";
9
9
 
10
- export const store = memoryApprovalStore({ ttl: 60 * 60 * 1000 }); // default: one hour
10
+ export const store = memoryApprovalStore({ ttl: 60 * 60 }); // seconds; default one hour
11
11
 
12
12
  // Every agent and HTTP adapter takes the same option
13
13
  export const { protect } = createPermDock(policy, { subject, store });
14
14
 
15
- // Routes for approvers: GET /pending, GET /mine, GET /:token, POST /:token/approve, POST /:token/reject
15
+ // Routes for approvers: GET /pending, GET /mine, GET /:id, GET /:id/signature, POST /:id/approve, POST /:id/reject
16
16
  const handler = approvalsHandler(store, {
17
17
  subject: (request) => approverFromSession(request), // the approver, from real authentication
18
18
  requireDistinctApprover: false, // true refuses the principal even where a grant sets distinct: false
@@ -22,6 +22,10 @@ const handler = approvalsHandler(store, {
22
22
  app.all("/permdock/approvals/*", (c) => handler(c.req.raw));
23
23
  ```
24
24
 
25
+ - `GET /:id` returns one request without its token, with a strong `ETag`; `If-None-Match` answers `304`. Unknown and foreign ids are `404`.
26
+ - `POST /:id/approve` and `POST /:id/reject` honour `If-Match` with that `ETag`. A stale tag is `412`, so two approvers on one screen cannot overwrite each other. Send `If-Match` from the confirmation screen.
27
+ - `GET /:id/signature` serves the signed approval (`vnd.permdock.approval+jwt`) when the handler has a `signer` and the request is approved, to the requester and the approvers only.
28
+
25
29
  The store's `ttl` is the window a request stays open when the call sets none; `resumeDecision({ ..., ttl })` sets it per call (milliseconds). A grant's `approval.ttl` only shortens either.
26
30
 
27
31
  - `memoryApprovalStore` is per process. On serverless or with several replicas, implement `ApprovalStore` over the app's database; the Drizzle recipe on the adapter page is the template, and `cloud().approvals` from `permdock/cloud` is the hosted implementation of the same interface. On Postgres or Supabase, set `rls.approvals: true` and use `supabaseApprovalStore(admin, { onOpen })` from `permdock/supabase` over the generated table and functions; grant the functions to the server's role, and put notifications and superseding older requests in `onOpen`.
@@ -33,10 +37,10 @@ The store's `ttl` is the window a request stays open when the call sets none; `r
33
37
  ## Approval policies as data
34
38
 
35
39
  ```ts
36
- import { memoryApprovalPolicies } from "permdock";
40
+ import { getPermDock, memoryApprovalPolicySource } from "permdock";
37
41
 
38
- const permdock = await createPermDock(policy, user, {
39
- approvalPolicies: memoryApprovalPolicies([
42
+ const permdock = await getPermDock(policy, user, {
43
+ approvalPolicies: memoryApprovalPolicySource([
40
44
  {
41
45
  permission: "expense.pay",
42
46
  tenant: "o_acme", // absent: every tenant
@@ -60,7 +64,9 @@ const permdock = await createPermDock(policy, user, {
60
64
  2. The runtime surfaces the ask (table below). A person answers through `approvalsHandler`, the runtime's own UI or the Cloud inbox.
61
65
  3. The original call is retried with the token. The adapter re-runs `decide`, recomputes the token, compares it, and calls `store.consume`. Only then does the tool or route run.
62
66
 
63
- The token binds the permission key, the resource id (or a digest of the data when there is no id), the principal, the tenant, the actor and the matched grant's conditions. Under `staleOn: 'resource-change'` it also binds the row's `version`.
67
+ The token is `sha-256:<base64url>`, a digest of the RFC 8785 JSON of the permission key, the resource id (or a digest of the data when there is no id), the principal, the tenant, the actor and the matched grant's conditions. Under `staleOn: 'resource-change'` it also binds the row's `version`. Treat it as opaque: compare it, never parse it.
68
+
69
+ A `DecisionSink` receives the request's lifecycle as CloudEvents `com.permdock.approval.requested`, `com.permdock.approval.resolved`, `com.permdock.approval.expired` and `com.permdock.approval.cancelled` ([wire formats](https://permdock.com/docs/concepts/wire-formats)). An event never approves; a consumer that acts on `resolved` still resumes through the store.
64
70
 
65
71
  A gate in your own code resumes the same way with `resumeDecision` from `permdock/approvals`, passing `token: carried ?? (await storedApprovalToken(store, decision))`: `storedApprovalToken` finds an approved or rejected request for the recomputed token, so the caller need not carry it. Do not reimplement it.
66
72
 
@@ -68,6 +74,14 @@ Over HTTP, the client retries the same request with the `PermDock-Approval: <tok
68
74
 
69
75
  A bad resume is denied with detail `approval-not-found`, `approval-pending`, `approval-rejected`, `approval-expired`, `approval-consumed` or `approval-mismatch`; a changed row is `stale-approval`. `approvalsHandler` answers `403` for an ineligible approver (`approver-not-eligible`, the actor, or the principal without `distinct: false`) and `409` for a repeated one (`approver-repeated`).
70
76
 
77
+ ## Delegation
78
+
79
+ Pass `approvalDelegations` (`memoryApprovalDelegationSource` or your table) and `subjectFor` to `approvalsHandler`. A delegate may approve what the delegator could, inside `from` (inclusive) and `until` (exclusive). The signature records `by` as the delegate and `onBehalfOf` plus `delegation` for the person covered. The store checks the delegator again. A link or a feed cannot approve. See [delegation](https://permdock.com/docs/adapters/approvals#delegation).
80
+
81
+ ## Access reviews
82
+
83
+ `startAccessReview` lists holders of a set of permissions. `decideAccessReviewItem` records keep or revoke. The application changes the role or membership, then marks the item applied. `accessReviewsHandler` serves the routes. See [access reviews](https://permdock.com/docs/concepts/access-reviews).
84
+
71
85
  ## Surfaces
72
86
 
73
87
  | Runtime | Ask | Resume |
@@ -87,7 +101,7 @@ For AG-UI event shapes, defer to the `ag-ui` spec skill (`npx skills add ScaleDo
87
101
 
88
102
  ## Delivery
89
103
 
90
- Notifying an approver is a listener on the `approval` event (`permdock.on('approval', …)`), never a package. A message or email carries only the token and links to a page where the approver signs in; the verdict goes through `approvalsHandler` with that session's subject. A link never approves on its own ([approvals adapter, Delivery](https://permdock.com/docs/adapters/approvals#delivery)).
104
+ Send the prompt with `approvalPrompt` and `permdock/delivery`: `approvalEmail` from `permdock/resend`, `approvalCard` and `approvalActions` from `permdock/chat-sdk`. A prompt carries the request id, never the token, and links to the app's approval page, where the approver signs in; the verdict goes through `approvalsHandler` with that session's subject. A link never approves. Details in [delivery-and-waiters.md](delivery-and-waiters.md#approval-prompts).
91
105
 
92
106
  ## Approvers
93
107
 
@@ -15,7 +15,7 @@ Read the app's `permissions.ts`, `policy.ts`, factory file and every adapter gua
15
15
  ## Inputs (find out, or ask before starting)
16
16
 
17
17
  - The scope: the whole app, or one pull request against its base branch.
18
- - Whether the `permdock` binary runs here (`permdock usage --json`, `permdock doctor --json`); otherwise walk the files and the catalog by hand.
18
+ - Whether the `permdock` binary runs here (`permdock usage --json`, `permdock doctor --json`; the report is in the result's `data`); otherwise walk the files and the catalog by hand.
19
19
  - Which topic areas the app uses: agent runtimes, approvals, named scopes and custom roles, ORM queries or RLS, tokens, API keys or share links.
20
20
 
21
21
  ## Invariants
@@ -40,7 +40,7 @@ Read the app's `permissions.ts`, `policy.ts`, factory file and every adapter gua
40
40
  ✓ Each check has a pass or a file-level finding.
41
41
  4. **Topic checks.** For each area the app uses, run the Verify list of its skill (`npx skills add ScaleDockHQ/PermDock --skill <name>`): `permdock-agents`, `permdock-approvals`, `permdock-tenancy`, `permdock-data`, `permdock-credentials`.
42
42
  ✓ Every item of every applicable Verify list has a pass or a finding.
43
- 5. **Tests.** No `describePolicy` suite, or one with `exhaustive: false`, is a finding: a new permission can ship untested. A custom `ApprovalStore`, `DecisionSink`, `MembershipSource`, `RoleSource`, `SnapshotSource`, `LimitStore`, `RelationSource` or `DirectoryStore` without its `test<Interface>` runner from `permdock/testing` is a finding.
43
+ 5. **Tests.** No `describePolicy` suite, or one with `exhaustive: false`, is a finding: a new permission can ship untested. A custom `ApprovalStore`, `DecisionSink`, `MembershipSource`, `CustomRoleSource`, `SnapshotSource`, `LimitStore`, `RelationSource` or `DirectoryStore` without its `test<Interface>` runner from `permdock/testing` is a finding.
44
44
  ✓ Every policy and custom store has its runner, or a finding.
45
45
  6. **ASI02 and ASI03.** Map the inventory onto [OWASP Agentic](https://permdock.com/docs/security/owasp-agentic); for the threat list itself, defer to the `owasp-agentic` spec skill (`npx skills add ScaleDockHQ/scaledock-skills --skill owasp-agentic`).
46
46
  - **ASI02** (tool misuse): unmapped tools, a collection permission on a row handler, a destructive tool without `approval`, capability lists that include denied tools.
@@ -50,7 +50,7 @@ Read the app's `permissions.ts`, `policy.ts`, factory file and every adapter gua
50
50
  - Run `permdock collect --check` and `permdock usage --json` on both sides and compare. New leaves with no grant, grants for leaves nobody checks, and new `undeclared` or `outsideInclude` findings are comments.
51
51
  - A new or widened `allow` (a role gains a leaf, a `where` is dropped or loosened, a `to` becomes `anyone()` or `authenticated()`), a new policy `delegations` entry, or a removed `deny` needs a matching `describePolicy` row in the same PR.
52
52
  - A new model-reachable tool, or a new destructive action on an existing one, goes through the `permdock-agents` and `permdock-approvals` Verify lists.
53
- - A change to the `rls` config or generated RLS comes with `permdock rls generate --check` passing and an `rlsParity` or `rls verify` run.
53
+ - A change to the `rls` config or generated RLS comes with `permdock rls generate --check` passing and an `testRlsParity` or `rls verify` run.
54
54
  - `permdock doctor --json` on the head branch reports no new error.
55
55
  ✓ Every changed grant, leaf and tool has a comment on the changed line or an explicit pass.
56
56
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: permdock-credentials
3
- description: Resolves PermDock subjects from access tokens, API keys and share links. Use when verifying OAuth or OIDC access tokens with subjectFromJwt or an introspection response with subjectFromIntrospection, issuing personal access tokens, API keys or service accounts with decideCredential and resolving them with subjectFromApiKey, setting tenant key rules (maxTtl, kinds, allowNoExpiry) in a SettingsSource, minting guest or share links with signCapability and resolving them with subjectFromCapability, serving a link through RLS with exchangeCapability, or fixing doctor PD029 and on('auth') causes such as invalid-chain.
3
+ description: Resolves PermDock subjects from access tokens, API keys and share links. Use when verifying OAuth or OIDC access tokens with subjectFromJwt or an introspection response with subjectFromIntrospection, issuing personal access tokens, API keys, service accounts or feed keys with decideCredential and resolving them with subjectFromApiKey, setting tenant key rules (maxTtl, kinds, allowNoExpiry) in a SettingsSource, minting guest or share links with signCapability and resolving them with subjectFromCapability, serving a link through RLS with exchangeCapability, or fixing doctor PD029 and on('auth') causes such as invalid-chain.
4
4
  license: MIT
5
5
  metadata:
6
6
  author: ScaleDockHQ
@@ -24,17 +24,17 @@ Authentication is upstream: core never verifies a token. A `subjectFrom*` resolv
24
24
  1. Core never verifies a token or a session. Only `permdock/jwt`, `permdock/server` key helpers and the provider `subjectFrom*` helpers do.
25
25
  2. A resolver never throws. An unverifiable token, key or link is the anonymous subject, and the cause goes to `on('auth')`.
26
26
  3. Never build grants from user-editable claims (`user_metadata`), and never take a subject from a request body, an unsigned header, a CLI flag or a model argument.
27
- 4. Store only `hashApiKey(key)` and show the key once. Every key lists its permissions and expires unless the tenant opts in with `allowNoExpiry`.
27
+ 4. Store only `hashApiKey(key)` and show the key once. A user or service key lists its permissions and expires unless the tenant opts in with `allowNoExpiry`. A feed key (`pdfeed_`) does not expire; rotation replaces the secret, and the key is bound to one resource.
28
28
  5. A share link is a signed, expiring capability on one resource. It never holds a scope or global role, and RLS sees it only through `exchangeCapability`, never `service_role`.
29
29
  6. Token rules come from the spec skills (`npx skills add ScaleDockHQ/scaledock-skills --skill <name>`): `jwt` for JOSE, `oauth` for access tokens and introspection, `openid-connect` for ID tokens and Discovery.
30
30
 
31
31
  ## Workflow
32
32
 
33
- 1. **Verify access tokens.** Create one `createJwtSubjectResolver({ discovery, audience })` per issuer from `permdock/jwt` for the life of the process, because it caches Discovery and the JWKS; `subjectFromJwt(token, options)` caches nothing and suits a one-off check. Or use `subjectFromIntrospection` when the app already called introspection. -> [references/tokens-keys-links.md](references/tokens-keys-links.md)
33
+ 1. **Verify access tokens.** Create one `jwtSubjectResolver({ discovery, audience })` per issuer from `permdock/jwt` for the life of the process, because it caches Discovery and the JWKS; `subjectFromJwt(token, options)` caches nothing and suits a one-off check. Or use `subjectFromIntrospection` when the app already called introspection. -> [references/tokens-keys-links.md](references/tokens-keys-links.md)
34
34
  ✓ An ID token, a token for another audience and an expired token each resolve to the anonymous subject.
35
35
  2. **Issue API keys, when the product has them.** Guard the creation endpoint with its own permission, call `decideCredential(permdock, request, { settings })`, store the hash with the returned `Credential`, and show the key once. -> [references/tokens-keys-links.md](references/tokens-keys-links.md#api-keys-and-service-accounts)
36
36
  ✓ A service key wider than its creator is denied with `exceeds-creator`.
37
- 3. **Resolve API keys.** Build `subjectFromApiKey({ verifier: apiKeyVerifier({ find }), permissions, owner, revoked, settings, sink })` and call it as `resolveKey(key, { tenant })`.
37
+ 3. **Resolve API keys.** Call `subjectFromApiKey(key, { verifier: apiKeyVerifier({ find }), permissions, principals, revoked, settings, sink, tenant })` on each request.
38
38
  ✓ A revoked or expired key is anonymous, and a user key never exceeds its owner's current rights.
39
39
  4. **Share links, when the product has them.** Declare the link's roles on the resource, mint with `signCapability` behind a guard of its own, and resolve with `subjectFromCapability` and the tenant's `linkPolicy`. With Supabase RLS, exchange the link with `exchangeCapability` and generate with `--capabilities`. -> [references/tokens-keys-links.md](references/tokens-keys-links.md#share-links)
40
40
  ✓ An expired, revoked or replayed (`once`) link resolves to the anonymous subject.
@@ -45,7 +45,7 @@ Authentication is upstream: core never verifies a token. A `subjectFrom*` resolv
45
45
 
46
46
  - [ ] `subjectFromJwt` uses Discovery or an explicit `jwks` plus `issuer`, sets `audience`, and allows only `Ed25519`, `ES256` or `PS256`.
47
47
  - [ ] No subject, tenant or membership comes from `user_metadata`, a request body or an unsigned header.
48
- - [ ] API keys are stored as hashes, created only through `decideCredential` behind their own guard, and expire (PD029).
48
+ - [ ] API keys are stored as hashes, created only through `permdock.decideCredential` behind their own guard, and expire (PD029).
49
49
  - [ ] No `memberships` source is passed to `createPermDock` for service-key subjects.
50
50
  - [ ] Share links expire, are minted behind their own guard, and reach RLS only through `exchangeCapability`.
51
51