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.
- package/README.md +37 -35
- package/dist/{openapi-schema--ARjyfQr.js → 2020-C9VN-x8c.js} +5 -132
- package/dist/a2a/index.d.ts +8 -6
- package/dist/a2a/index.js +21 -31
- package/dist/{adapter-context-mlWsVGGg.js → adapter-context-CRhqyzCk.js} +1 -1
- package/dist/adoption-scan-B4R5fnMU.js +107 -0
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.js +1 -0
- package/dist/ai-sdk/index.d.ts +18 -23
- package/dist/ai-sdk/index.js +38 -21
- package/dist/app-data-MuyzVqX8.js +60 -0
- package/dist/approval-header-CJACtHkv.js +23 -0
- package/dist/approval-prompt-Dg5LeAcT.js +62 -0
- package/dist/approval-prompt-Djo_PyZc.d.ts +57 -0
- package/dist/approvals/index.d.ts +5 -3
- package/dist/approvals/index.js +8 -5
- package/dist/approvals-DJ6Yd5z1.js +995 -0
- package/dist/{approvers-DT4AVyIg.js → approvers-CqPOLD-o.js} +1 -1
- package/dist/arazzo-B6lebmiQ.js +36 -0
- package/dist/{arazzo-BAgw9ap6.js → arazzo-BWhNF-k0.js} +24 -17
- package/dist/args-IIolxxKA.js +197 -0
- package/dist/astro/index.d.ts +43 -0
- package/dist/astro/index.js +27 -0
- package/dist/audience-Dg8XsRaO.js +55 -0
- package/dist/auth-DRLEizbL.js +178 -0
- package/dist/authzen/index.d.ts +26 -6
- package/dist/authzen/index.js +228 -75
- package/dist/automation-BMZPIVUM.d.ts +114 -0
- package/dist/automation-DVjOckjV.js +157 -0
- package/dist/better-auth/index.d.ts +8 -6
- package/dist/better-auth/index.js +11 -23
- package/dist/better-supabase/index.d.ts +438 -86
- package/dist/better-supabase/index.js +1027 -184
- package/dist/bind--9EFVYWF.d.ts +38 -0
- package/dist/{bind-CuzGytRf.js → bind-CZzhx-Vj.js} +22 -10
- package/dist/body-CH5of8Ne.js +10 -0
- package/dist/body-text-QcXok_SI.js +52 -0
- package/dist/build-instance-BMnAudHn.js +52 -0
- package/dist/{canonical-json-DemiK4Vp.js → canonical-json-BC8_nMTw.js} +6 -6
- package/dist/{capability-2kYTMWDv.d.ts → capability-BSslw1QV.d.ts} +44 -25
- package/dist/{capability-4qPdO9gk.js → capability-N1K9IpqS.js} +69 -42
- package/dist/casbin-Cr7RLtA3.js +30 -0
- package/dist/casbin-Dyu2gnUD.js +234 -0
- package/dist/casl-64gpT-8x.js +24 -0
- package/dist/casl-DjEDhTCP.js +299 -0
- package/dist/catalog/index.d.ts +74 -2
- package/dist/catalog/index.js +51 -2
- package/dist/catalog-BECMPrwc.js +100 -0
- package/dist/catalog-DjhMYYCq.js +1 -0
- package/dist/catalog-R_A__RsH.js +52 -0
- package/dist/{catalog-doc-DdGm82XT.js → catalog-doc-C9wjsfNY.js} +54 -25
- package/dist/catalog-fingerprint-CCP0-yQS.js +153 -0
- package/dist/chat/index.d.ts +2 -0
- package/dist/chat/index.js +224 -0
- package/dist/chat-sdk/index.d.ts +87 -0
- package/dist/chat-sdk/index.js +197 -0
- package/dist/claims-map-7oQ3ue4X.js +13 -0
- package/dist/claims-map-Ca-QX8C6.d.ts +32 -0
- package/dist/claude-agent/index.d.ts +7 -14
- package/dist/claude-agent/index.js +26 -30
- package/dist/clerk/index.d.ts +9 -8
- package/dist/clerk/index.js +15 -45
- package/dist/cli/bin.js +36 -17
- package/dist/cli/index.d.ts +20 -6
- package/dist/cli/index.js +164 -4
- package/dist/cloud/index.d.ts +102 -63
- package/dist/cloud/index.js +240 -347
- package/dist/cloud-CIoHNevm.js +87 -0
- package/dist/codemod-Bmj9dWMH.js +37 -0
- package/dist/codemod-Cxvvc70T.js +2084 -0
- package/dist/{collect-DSTE3ml9.js → collect-CrZJJnEF.js} +79 -17
- package/dist/collect-DloHbDVa.js +55 -0
- package/dist/collect-FlXJlhus.js +1 -0
- package/dist/command-L8EstFNm.js +6 -0
- package/dist/{compact-y6vfu2_f.js → compact-CAf557ZB.js} +11 -1
- package/dist/compile/index.d.ts +1 -1
- package/dist/compile/index.js +1 -2
- package/dist/{compile-Ba4blROR.js → compile-CMqMx_IN.js} +13 -9
- package/dist/{compile-BkRyBDBw.d.ts → compile-Ckm2HqBZ.d.ts} +5 -3
- package/dist/completion-BqhRBJD8.js +143 -0
- package/dist/config-B46rm9sc.js +26 -0
- package/dist/{config-BJiM-xAm.js → config-bs_kZeYq.js} +15 -10
- package/dist/{config-report-CdCgbRnV.js → config-report-DUgj62Ai.js} +17 -8
- package/dist/{connection-CU8MQxRi.js → connection-DjhIJSs2.js} +8 -6
- package/dist/constants-DcJABbLW.js +20 -0
- package/dist/{context-Bcy85rGi.js → context-bbFHR-0D.js} +16 -5
- package/dist/convex/index.d.ts +11 -10
- package/dist/convex/index.js +18 -18
- package/dist/create-0VZ7Pdiu.d.ts +107 -0
- package/dist/create-BDS7wc3y.d.ts +138 -0
- package/dist/create-BFfm17UA.d.ts +110 -0
- package/dist/create-BWRSXgOF.js +403 -0
- package/dist/create-C3b4B5eF.js +130 -0
- package/dist/create-DnrmLnLW.js +323 -0
- package/dist/{credential-8P81ff4u.js → credential-DM2um3ha.js} +153 -32
- package/dist/credentials-DPyHmYz5.js +74 -0
- package/dist/deciding-columns-Sq12mEA6.js +123 -0
- package/dist/delegation-DaXVdDhM.js +286 -0
- package/dist/delegation-input-FTxMK3ji.js +19 -0
- package/dist/delivery/index.d.ts +5 -0
- package/dist/delivery/index.js +5 -0
- package/dist/delivery-DCAgNyCn.js +499 -0
- package/dist/delivery-event-xX4obB8F.js +40 -0
- package/dist/denied-text-D8jDHgsl.d.ts +19 -0
- package/dist/{describe-DMYiCjJV.d.ts → describe-BZUsixiL.d.ts} +11 -2
- package/dist/{describe-B_gVZEVi.js → describe-BybMRY7A.js} +10 -5
- package/dist/device-BARQ8eYG.js +193 -0
- package/dist/diff-CxZo9pTq.js +37 -0
- package/dist/{diff-2VaeChzX.js → diff-DCePiN6M.js} +61 -55
- package/dist/doctor-CxiwSfqH.js +44 -0
- package/dist/doctor-DFjUxf2f.js +2361 -0
- package/dist/drizzle/index.d.ts +5 -3
- package/dist/drizzle/index.js +6 -14
- package/dist/elysia/index.d.ts +16 -13
- package/dist/elysia/index.js +14 -13
- package/dist/env-DJZrPjRi.js +8 -0
- package/dist/errors-BDUwg2qC.js +27 -0
- package/dist/{errors-Cz5zn0mW.js → errors-BKy0EfhN.js} +6 -10
- package/dist/errors-BWXBMaKK.d.ts +11 -0
- package/dist/{errors-DPdqI50v.d.ts → errors-C2PXIv4c.d.ts} +2 -2
- package/dist/errors-CWTY6uih.js +143 -0
- package/dist/evaluate-_R8QkWaf.js +1732 -0
- package/dist/evaluations-16UqqQ6W.d.ts +32 -0
- package/dist/{evaluations-tz6D9A2G.js → evaluations-DZ2CbV5k.js} +181 -32
- package/dist/eve/index.d.ts +12 -16
- package/dist/eve/index.js +23 -29
- package/dist/event-time-CsegxXxB.js +23 -0
- package/dist/expiry-D4_YKRKD.js +52 -0
- package/dist/express/index.d.ts +13 -10
- package/dist/express/index.js +14 -12
- package/dist/fastify/index.d.ts +26 -13
- package/dist/fastify/index.js +17 -36
- package/dist/fetch-adapter-g0ypLkID.js +120 -0
- package/dist/fetch-adapter-qKJBuRqy.d.ts +31 -0
- package/dist/fields-D9tPVUUS.js +203 -0
- package/dist/files-C5ZI27eV.js +124 -0
- package/dist/{files-DWUN9PzI.js → files-D_JIzUrP.js} +12 -3
- package/dist/{fixtures-ht28Cplr.js → fixtures-B7XrLeY7.js} +12 -12
- package/dist/flags-C3DktBDY.js +86 -0
- package/dist/{from-snapshot-CTLijvdm.js → from-snapshot-D2lZt4Dd.js} +78 -65
- package/dist/{generate-CH7ThJ14.js → generate-CNU1pIOo.js} +1 -3
- package/dist/grantee-Cn1HyUsI.js +254 -0
- package/dist/grantee-conditions-CcXf8hMM.js +271 -0
- package/dist/grantee-match-DHArqY82.js +142 -0
- package/dist/h3/index.d.ts +37 -0
- package/dist/h3/index.js +35 -0
- package/dist/headers-BIjfMaoC.js +8 -0
- package/dist/headers-CIbDTUTK.d.ts +7 -0
- package/dist/{helpers-H_KARDVC.js → helpers-DTcZOzhF.js} +47 -39
- package/dist/hono/index.d.ts +12 -9
- package/dist/hono/index.js +14 -13
- package/dist/{hosted-DJQw46hn.js → hosted-BZIAYvon.js} +64 -15
- package/dist/hosted-DBDX0M-P.d.ts +78 -0
- package/dist/{http-SO7qGttf.js → http-BBoKefeS.js} +35 -2
- package/dist/{http-DPuDaChb.d.ts → http-B_LphE5Y.d.ts} +1 -2
- package/dist/http-DXJ37RDA.js +46 -0
- package/dist/{http-BIhmLfCc.js → http-ujKm0JXE.js} +3 -26
- package/dist/{openapi-import-cmwetbzw.js → import-CNV7I2Dw.js} +30 -26
- package/dist/{rls-import-4lLW_pbb.js → import-DMmOG27O.js} +271 -128
- package/dist/index-BcHcPyIo.d.ts +110 -0
- package/dist/index-CaxrMD1A.d.ts +1 -0
- package/dist/index-Chrs5fQ-.d.ts +210 -0
- package/dist/index-CxGUswIC.d.ts +431 -0
- package/dist/index-D8EovfsP.d.ts +91 -0
- package/dist/index-DOVf7RJv.d.ts +900 -0
- package/dist/{index-C8z4RjQV.d.ts → index-SGdIQyzc.d.ts} +34 -16
- package/dist/index.d.ts +20 -14
- package/dist/index.js +33 -23
- package/dist/init-DeMYsnYJ.js +96 -0
- package/dist/init-GG6Jh8HD.js +95 -0
- package/dist/{instance-options-jARdwfPN.d.ts → instance-options-Omfd793B.d.ts} +2 -2
- package/dist/instance-snapshot-Cxq4ygFc.js +600 -0
- package/dist/instant-BluqDvyS.js +19 -0
- package/dist/internals-Dk8A8fX3.js +17 -0
- package/dist/{json-schema-BsAflUGW.js → json-schema-B-vAwlde.js} +46 -9
- package/dist/json-schema-CN_iLFH2.d.ts +40 -0
- package/dist/jwt/index.d.ts +18 -11
- package/dist/jwt/index.js +166 -145
- package/dist/{kernel-_RRHmnMY.js → kernel-5JatXyND.js} +120 -43
- package/dist/kysely/index.d.ts +5 -3
- package/dist/kysely/index.js +6 -14
- package/dist/{live-options-H2eG8dfJ.js → live-options-BaAFCIiu.js} +16 -3
- package/dist/load-BxEtwlBS.js +1 -0
- package/dist/{load-DhWEB9P1.js → load-DFS_ry0W.js} +31 -7
- package/dist/{load-jose-BL6LH4bL.js → load-jose-DZ4dnjri.js} +1 -4
- package/dist/login-Dfe5RlYk.js +68 -0
- package/dist/login-imhuwvn3.js +254 -0
- package/dist/manifest-C8IzW3Do.js +42 -0
- package/dist/manifest-CtKR3GXL.js +370 -0
- package/dist/manifest-json-CAGzls-C.js +9 -0
- package/dist/{manifest-uDS9y5de.d.ts → manifest-mx5ghSFM.d.ts} +9 -2
- package/dist/manifest-schema-C0kK_8V0.js +399 -0
- package/dist/{map-claims-Z4QZOiFF.js → map-claims-D3oRmTk0.js} +19 -25
- package/dist/{map-error-pgUWNNtG.js → map-error-TCAdw-eW.js} +5 -5
- package/dist/{markers-B0hYLvMD.js → markers-C6joDI3o.js} +8 -3
- package/dist/mcp/index.d.ts +36 -9
- package/dist/mcp/index.js +476 -448
- package/dist/{rls-migrate-CN4TlJTO.js → migrate-BShP7Vf4.js} +119 -20
- package/dist/nest/index.d.ts +12 -9
- package/dist/nest/index.js +20 -20
- package/dist/next/index.d.ts +40 -20
- package/dist/next/index.js +37 -39
- package/dist/next/plugin.d.ts +1 -1
- package/dist/next/plugin.js +2 -2
- package/dist/node/index.d.ts +10 -10
- package/dist/node/index.js +7 -9
- package/dist/ocsf-BFp-PZZA.js +126 -0
- package/dist/openai/index.d.ts +9 -16
- package/dist/openai/index.js +18 -25
- package/dist/openapi/index.d.ts +3 -3
- package/dist/openapi/index.js +1 -1
- package/dist/{openapi-BqRBvAng.js → openapi-B7HZ0f-r.js} +9 -12
- package/dist/{openapi-CqqEbHsL.js → openapi-DTaEmTSW.js} +23 -26
- package/dist/openapi-ahr8rHu3.js +138 -0
- package/dist/operation-findings-DgATDpvh.js +36 -0
- package/dist/operations-CxybZErY.d.ts +59 -0
- package/dist/orpc/index.d.ts +13 -10
- package/dist/orpc/index.js +23 -20
- package/dist/otel/index.d.ts +3 -2
- package/dist/otel/index.js +53 -27
- package/dist/output-Dyw8fyPF.js +88 -0
- package/dist/output-files-B3IZJksC.js +1 -0
- package/dist/output-files-DhRBBE4n.js +138 -0
- package/dist/{rls-fields-CZOMlwHl.js → ownership-BatFgAA4.js} +2393 -2349
- package/dist/{package-root-zGkxO3oN.js → package-root-Dk_ETdkp.js} +4 -3
- package/dist/{parse-BuyjxhZV.js → parse-Cxq0WxrA.js} +3 -3
- package/dist/{parse-local-manifest-BrMozJCo.js → parse-local-manifest-CVSs6J9I.js} +2 -4
- package/dist/{parse-local-manifest-JLnaBH0S.d.ts → parse-local-manifest-DgqtruPn.d.ts} +1 -1
- package/dist/parse-request-D_tUXG6M.js +26 -0
- package/dist/paths-DXSHAMIa.js +42 -0
- package/dist/pdp/index.d.ts +7 -3
- package/dist/pdp/index.js +47 -29
- package/dist/{peer-BOr_kJE1.js → peer-C8swJ2GH.js} +23 -4
- package/dist/peer-CRvC7Hg1.js +1 -0
- package/dist/permdock-B9JxkBwC.js +868 -0
- package/dist/permdock-D7eeLJER.d.ts +502 -0
- package/dist/permission-registry-Br4jSNVi.js +385 -0
- package/dist/permissions-B-wCP5ZC.js +30 -0
- package/dist/{permissions-RJrovw98.js → permissions-CZKiUfFM.js} +4 -3
- package/dist/{permissions-CDgvKG5L.d.ts → permissions-oEWfl-kl.d.ts} +1 -2
- package/dist/policies-CO1oOymN.js +295 -0
- package/dist/{policy-D2W7uv19.d.ts → policy-Czo4n3Hs.d.ts} +2 -2
- package/dist/policy-DLZJ5gAp.js +399 -0
- package/dist/policy-from-catalog-DzhEc3Y9.js +450 -0
- package/dist/policy-module-CTlwMvLI.js +89 -0
- package/dist/{policy-IB6yxpC3.d.ts → policy-types-DzwivBgR.d.ts} +939 -1057
- package/dist/postgrest-CW0IqC-U.js +566 -0
- package/dist/postgrest-NUqZT-RB.d.ts +100 -0
- package/dist/powersync-BHfuehf3.js +62 -0
- package/dist/powersync-CbzkqL_M.js +1 -0
- package/dist/{powersync-Dv1_aWF7.js → powersync-bYUDLOAO.js} +30 -32
- package/dist/preset/index.d.ts +146 -0
- package/dist/preset/index.js +1 -0
- package/dist/preset-DZdU0tne.js +1768 -0
- package/dist/principal-source-pHneukrx.js +103 -0
- package/dist/prisma/index.d.ts +6 -3
- package/dist/prisma/index.js +6 -12
- package/dist/{problem-B43ztKIm.js → problem-CTviyH5L.js} +18 -27
- package/dist/{problem-details-DX075l9V.d.ts → problem-details-DXlt-vks.d.ts} +1 -1
- package/dist/{problem-details-_AMOYz-w.js → problem-details-cn5X30gZ.js} +45 -42
- package/dist/problem-types-CJ2325SZ.js +5 -0
- package/dist/{prompt-CXYhLfsS.js → prompt-DfZLJLIn.js} +2 -2
- package/dist/protected-DEPHc2xV.d.ts +38 -0
- package/dist/{protected-utU-SLuy.js → protected-DfjY5mLA.js} +37 -8
- package/dist/{provider-CSQd7m92.js → provider-CC-u3bYP.js} +23 -10
- package/dist/{provider-DZY281jO.d.ts → provider-DMVt6yYn.d.ts} +1 -1
- package/dist/{provider-store-Cfbvo4DE.js → provider-store-B5Yrkhzr.js} +6 -3
- package/dist/pull-BgIfno6w.js +256 -0
- package/dist/pull-ByOks_Tg.js +1 -0
- package/dist/push-CmFVBOeI.js +116 -0
- package/dist/pushed-policy-BImMTTIM.d.ts +57 -0
- package/dist/pushed-policy-DobP7AHI.js +238 -0
- package/dist/react/index.d.ts +8 -8
- package/dist/react/index.js +6 -6
- package/dist/react/provider-client.d.ts +1 -1
- package/dist/react/provider-client.js +1 -2
- package/dist/react-native/index.d.ts +24 -63
- package/dist/react-native/index.js +45 -27
- package/dist/{refs-KYb4ntaz.js → refs-K6eWUJ0H.js} +1 -1
- package/dist/relation-walk-3OyDBF0S.js +186 -0
- package/dist/relations-BnCWnJGI.js +163 -0
- package/dist/renames-CGj7YwcQ.js +175 -0
- package/dist/{replay-CYyiLiwO.d.ts → replay-D7iKcLFe.d.ts} +1 -1
- package/dist/{replay-BsOyTrdV.js → replay-Du1tJjG7.js} +2 -1
- package/dist/request-context-BTRTG7OT.js +71 -0
- package/dist/request-context-C2_1lchh.d.ts +5 -0
- package/dist/request-schema-CMreVuFM.js +148 -0
- package/dist/resend/index.d.ts +126 -0
- package/dist/resend/index.js +379 -0
- package/dist/resource-relations-D_jw0BFx.js +243 -0
- package/dist/retire-CALjIQ7e.js +240 -0
- package/dist/{revocations-Cy06JNx2.d.ts → revocations-R07lFBnX.d.ts} +2 -0
- package/dist/rls-BA8an5Ap.js +336 -0
- package/dist/{rls-DiinlRML.js → rls-j6t43t7n.js} +1066 -1068
- package/dist/role-builder-CXbis5-X.js +623 -0
- package/dist/{rpc-DL9Sqvb1.js → rpc-aPyNi6tA.js} +18 -8
- package/dist/run-CNsApDpd.js +1 -0
- package/dist/run-DOfMtc9V.js +723 -0
- package/dist/run-ticket-BjhTOi6u.d.ts +124 -0
- package/dist/run-ticket-sxFI6LAk.js +337 -0
- package/dist/{runtime-CB8gQFvS.js → runtime-D3VTAiCA.js} +1 -1
- package/dist/{saas-DhpJjnBA.js → saas-FO-Q8oj0.js} +18 -15
- package/dist/scan-BOI5tJ-6.js +29 -0
- package/dist/{schema-D3MnDMxa.js → schema-B4v0Qrac.js} +106 -10
- package/dist/schema-yLuBWWsh.js +130 -0
- package/dist/scim/index.d.ts +2 -2
- package/dist/scim/index.js +1 -2
- package/dist/{scim-BFGBBAqR.js → scim-ClHtpyV3.js} +213 -87
- package/dist/{scopes-CVdPrWN-.js → scopes-CUPSjo9q.js} +20 -7
- package/dist/server/index.d.ts +13 -134
- package/dist/server/index.js +13 -10
- package/dist/server-DqCT4CgZ.js +453 -0
- package/dist/{create-B1d519XK.d.ts → server-types-BLd4MnBX.d.ts} +89 -70
- package/dist/{sha256-BO0swRXL.js → sha256-D9ihqmvd.js} +8 -1
- package/dist/shared-CD11nBbC.js +304 -0
- package/dist/{shared-DRu8VFVM.js → shared-DvvFzYXe.js} +8 -9
- package/dist/signals-D3NcTKUS.js +32 -0
- package/dist/sink-BEZSlMS_.js +957 -0
- package/dist/sink-Bj_lDUDq.d.ts +225 -0
- package/dist/{skills-Biq5uup3.js → skills-B6lVdD_W.js} +16 -8
- package/dist/skills-CTYwkl44.js +51 -0
- package/dist/snapshot-CXUWfsNy.js +269 -0
- package/dist/{snapshot-cache-D6jLelln.js → snapshot-cache-DZHQ0KF6.js} +11 -5
- package/dist/{snapshot-cache-B-LxUahj.d.ts → snapshot-cache-DqK9eDmM.d.ts} +7 -5
- package/dist/solid/index.d.ts +24 -25
- package/dist/solid/index.js +41 -11
- package/dist/solid-start/index.d.ts +52 -0
- package/dist/solid-start/index.js +49 -0
- package/dist/{sources-BdOYLw9j.d.ts → sources-BV9j0DeZ.d.ts} +11 -13
- package/dist/{sources-4p0-4xQm.js → sources-vs5dGOsS.js} +147 -142
- package/dist/sql-B5T1IL83.js +55 -0
- package/dist/src-DLfUWFAI.js +1851 -0
- package/dist/ssf/index.d.ts +4 -4
- package/dist/ssf/index.js +440 -106
- package/dist/standard-webhooks-6lg8v_is.d.ts +88 -0
- package/dist/standard-webhooks-DgFFD-Ga.js +228 -0
- package/dist/storage-C2bWdvJH.js +132 -0
- package/dist/{store-DMAWwAJV.js → store-4u_hxjWa.js} +42 -55
- package/dist/{store-options-ByhZq1jV.js → store-options-BqEakYRa.js} +38 -24
- package/dist/{stores-BT2pSCub.js → stores-BbsD4fAR.js} +7 -3
- package/dist/stores-ClZUgeoP.d.ts +30 -0
- package/dist/{stream-D1RXubw2.d.ts → stream-7-xmnf3U.d.ts} +2 -2
- package/dist/{stream-DDpvynSB.js → stream-CvdUS9V1.js} +10 -2
- package/dist/style-DiKWnAvn.js +1 -0
- package/dist/{style-CTm1AXDT.js → style-Uao76wO7.js} +3 -2
- package/dist/subject-BGoM5o8B.js +17 -0
- package/dist/{subject-settings-BwBmCdTR.js → subject-settings-CkEwrw3W.js} +23 -5
- package/dist/supabase/index.d.ts +23 -106
- package/dist/supabase/index.js +5 -4
- package/dist/supabase/middleware.d.ts +13 -6
- package/dist/supabase/middleware.js +16 -11
- package/dist/supabase-9pqI57pW.js +92 -0
- package/dist/{supabase-C0qESOa-.js → supabase-Dxlp8A94.js} +124 -247
- package/dist/supabase-hook-CXcGpHqq.js +1 -0
- package/dist/{supabase-hook-ntETYCQu.js → supabase-hook-DdDQhMlw.js} +1659 -819
- package/dist/{supautils-u8jXTGrt.js → supautils-B6HYL9eL.js} +2 -2
- package/dist/support-session-C6xJQYut.js +158 -0
- package/dist/svelte/Protected.svelte +11 -4
- package/dist/svelte/index.d.ts +7 -7
- package/dist/svelte/index.js +26 -12
- package/dist/svelte/runtime.d.ts +10 -3
- package/dist/svelte/runtime.js +4 -3
- package/dist/svelte/source.d.ts +7 -7
- package/dist/svelte/source.js +5 -5
- package/dist/sveltekit/index.d.ts +51 -0
- package/dist/sveltekit/index.js +27 -0
- package/dist/tanstack-start/index.d.ts +51 -0
- package/dist/tanstack-start/index.js +40 -0
- package/dist/target-Bw37TFNA.js +51 -0
- package/dist/{validity-uOp8UpYn.js → tenancy-DN2QAx9z.js} +6 -42
- package/dist/terminal/index.d.ts +11 -9
- package/dist/terminal/index.js +36 -316
- package/dist/testing/index.d.ts +391 -106
- package/dist/testing/index.js +4574 -1286
- package/dist/testing/saas/index.d.ts +9 -8
- package/dist/testing/saas/index.js +2 -2
- package/dist/testing/saas/permissions.d.ts +1 -1
- package/dist/testing/saas/permissions.js +1 -2
- package/dist/text-BMFRfBqc.js +8 -0
- package/dist/{text-diff-BdkkYSbi.js → text-diff-BQcUapOb.js} +1 -1
- package/dist/thenable-yD7SabqG.js +31 -0
- package/dist/time-DkWjdL98.js +71 -0
- package/dist/{token-CIWxJOCh.js → token-BLZcjQgt.js} +15 -6
- package/dist/tool-refusal-CMLN5xhn.js +41 -0
- package/dist/trpc/index.d.ts +12 -9
- package/dist/trpc/index.js +18 -17
- package/dist/{types-DbjRN55U.d.ts → types-5Ut_pbKy.d.ts} +141 -13
- package/dist/types-BNORmDM8.d.ts +172 -0
- package/dist/types-BjV-fqcA.d.ts +57 -0
- package/dist/{types-QCuYJh4q.d.ts → types-BqAKcRNW.d.ts} +2 -1
- package/dist/types-C8-KNsFf.d.ts +107 -0
- package/dist/{types-aqOHTHcp.d.ts → types-CCpc2Dcv.d.ts} +77 -17
- package/dist/{types-Dnemwd77.d.ts → types-CZTnPFzM.d.ts} +50 -11
- package/dist/{types-C97IskBw.d.ts → types-CdlxsYQh.d.ts} +23 -11
- package/dist/{types-DxiCGmmd.d.ts → types-ClZaWzfb.d.ts} +9 -6
- package/dist/types-DX0LaKTb.d.ts +157 -0
- package/dist/types-DiQjupG2.d.ts +332 -0
- package/dist/types-DqzsnlSA.js +67 -0
- package/dist/types-_sHy8BQf.d.ts +65 -0
- package/dist/types-fbZ7JEXr.d.ts +89 -0
- package/dist/types-tIgIeIKJ.d.ts +66 -0
- package/dist/unplugin/index.d.ts +1 -1
- package/dist/unplugin/index.js +2 -2
- package/dist/usage-CHOtwL19.js +1 -0
- package/dist/usage-DHtE8j4E.js +32 -0
- package/dist/{usage-ItS7nLcl.js → usage-T5mXq98v.js} +56 -13
- package/dist/{validation-error-Ba264OaJ.d.ts → validation-error-Bg5Tpluk.d.ts} +1 -1
- package/dist/{validation-error-CnjZ4t92.js → validation-error-ZfUHbKvf.js} +1 -1
- package/dist/validity-CZ7-y0Cj.js +79 -0
- package/dist/{verifier-BxBo6I44.js → verifier-DHuh-okk.js} +68 -21
- package/dist/verify-DbCL9bnB.js +1770 -0
- package/dist/{version-BvacVTZR.js → version-CEbQ5UeM.js} +4 -3
- package/dist/version-DXkYUISx.js +1 -0
- package/dist/{views-Soe7bGSC.js → views-DTIIO-oQ.js} +41 -2
- package/dist/{permissions-DttnqqxC.d.ts → vocabulary-D6lPdb4Z.d.ts} +120 -29
- package/dist/vue/index.d.ts +25 -21
- package/dist/vue/index.js +25 -9
- package/dist/waiter-BGUtGYoU.js +67 -0
- package/dist/waiter-CKy4MQSY.d.ts +36 -0
- package/dist/watch-Cb-NcaUw.js +1 -0
- package/dist/{watch-DVBJ9cBq.js → watch-CszoTJMq.js} +47 -8
- package/dist/webmcp/index.d.ts +2 -2
- package/dist/webmcp/index.js +13 -37
- package/dist/where-scope-Dv-WTP-w.js +306 -0
- package/dist/who-can-D0QoYbEb.js +364 -0
- package/dist/wire-delegation-Cv3W2eaM.js +36 -0
- package/dist/wire-denial-C7PaVYEM.js +27 -0
- package/dist/workflow/index.d.ts +5 -0
- package/dist/workflow/index.js +223 -0
- package/dist/workflow-sdk/index.d.ts +56 -0
- package/dist/workflow-sdk/index.js +78 -0
- package/dist/wrap-Bd1Tejur.d.ts +15 -0
- package/dist/wrap-gX61EtpB.js +27 -0
- package/package.json +140 -41
- package/schemas/access-event-v1.json +118 -0
- package/schemas/approval-request-v1.json +34 -1
- package/schemas/approval-signature-v1.json +94 -0
- package/schemas/automation-event-v1.json +113 -0
- package/schemas/capability-v1.json +84 -0
- package/schemas/catalog-event-v1.json +87 -0
- package/schemas/catalog-v1.json +129 -12
- package/schemas/cli-commands-v1.json +164 -0
- package/schemas/cli-result-v1.json +96 -0
- package/schemas/cloud-api-v1.json +5407 -0
- package/schemas/connector-v1.json +408 -0
- package/schemas/credential-event-v1.json +81 -0
- package/schemas/credential-v1.json +134 -0
- package/schemas/decision-event-v1.json +288 -0
- package/schemas/delivery-event-v1.json +117 -0
- package/schemas/directory-event-v1.json +85 -0
- package/schemas/evidence-export-v1.json +54 -0
- package/schemas/membership-event-v1.json +93 -0
- package/schemas/policy-v1.json +68 -0
- package/schemas/pushed-policy-v1.json +236 -0
- package/schemas/run-v1.json +162 -0
- package/schemas/snapshot-v1.json +138 -0
- package/schemas/supabase-claims-v1.json +4 -4
- package/schemas/supabase-manifest-v1.json +5 -1
- package/schemas/support-session-v1.json +111 -0
- package/skills/permdock/SKILL.md +16 -12
- package/skills/permdock-agents/SKILL.md +40 -23
- package/skills/permdock-agents/references/operations-automations-chat.md +221 -0
- package/skills/permdock-agents/references/runtimes.md +22 -18
- package/skills/permdock-approvals/SKILL.md +29 -13
- package/skills/permdock-approvals/references/delivery-and-waiters.md +172 -0
- package/skills/permdock-approvals/references/stores-and-resume.md +21 -7
- package/skills/permdock-audit/SKILL.md +3 -3
- package/skills/permdock-credentials/SKILL.md +5 -5
- package/skills/permdock-credentials/references/tokens-keys-links.md +19 -10
- package/skills/permdock-data/SKILL.md +6 -6
- package/skills/permdock-data/references/queries-and-rls.md +13 -8
- package/skills/permdock-data/references/relationships.md +3 -3
- package/skills/permdock-tenancy/SKILL.md +20 -7
- package/skills/permdock-tenancy/references/contacts-and-support-sessions.md +122 -0
- package/skills/permdock-tenancy/references/ownership-and-custom-roles.md +6 -4
- package/skills/permdock-wire/SKILL.md +18 -12
- package/skills/permdock-wire/references/adapters.md +146 -50
- package/skills/permdock-wire/references/policy-options.md +15 -6
- package/skills/permdock-wire/references/presets-and-recipes.md +58 -0
- package/dist/approvals-DbYAuNkG.js +0 -224
- package/dist/arazzo-C9chp3ST.js +0 -54
- package/dist/bind-C_ob3LKa.d.ts +0 -25
- package/dist/catalog-CRW5Mx_B.js +0 -2
- package/dist/catalog-CYj-mX2O.js +0 -48
- package/dist/catalog-DWF5cYqB.js +0 -55
- package/dist/cloud-CfwZ39Rl.js +0 -50
- package/dist/cloud-DWLXO2af.js +0 -118
- package/dist/collect-CHB22Sx4.js +0 -83
- package/dist/collect-DSWOk19R.js +0 -2
- package/dist/commands-BeaGNj_o.js +0 -97
- package/dist/config-ir40y8m0.js +0 -36
- package/dist/create-CMwIl-w2.js +0 -253
- package/dist/delegation-D8pUEuce.js +0 -103
- package/dist/diff-C9RDjjhs.js +0 -48
- package/dist/doctor-Djh6JDEk.js +0 -53
- package/dist/doctor-DvH0IQff.js +0 -1194
- package/dist/doctor-collect-CjdO0wQX.js +0 -665
- package/dist/duration-xK2jGFVc.js +0 -25
- package/dist/errors-CWfoNg9N.js +0 -47
- package/dist/errors-DRnGZHzF.js +0 -30
- package/dist/evaluation-items-CfCCejQd.js +0 -40
- package/dist/expiry-BoBJ8Tvc.js +0 -7
- package/dist/fields-BMl1RxoX.js +0 -26
- package/dist/grantee-Cv5Sogfv.js +0 -602
- package/dist/headers-BZSDieNT.js +0 -6
- package/dist/headers-Cf-zWvsO.d.ts +0 -6
- package/dist/ids-DC4V4CFu.js +0 -18
- package/dist/index-0nNqHspG.d.ts +0 -6
- package/dist/index-BNc7Wer_.d.ts +0 -483
- package/dist/index-C7Tw7MaW.d.ts +0 -201
- package/dist/instance-options-Dhhcnm3y.js +0 -22
- package/dist/load-DwJUafYN.js +0 -2
- package/dist/manifest-iaoBXHKc.js +0 -783
- package/dist/map-AcxK9o_D.js +0 -147
- package/dist/may-use-DndQ5tQ-.js +0 -77
- package/dist/openapi-CQTsgKoB.js +0 -164
- package/dist/permdock-CbgvLnuo.js +0 -3479
- package/dist/permissions-DVoF6dMt.js +0 -662
- package/dist/policy-xyWg7QdR.js +0 -1122
- package/dist/powersync-CrBX8hY6.js +0 -2
- package/dist/powersync-DE19b9dT.js +0 -62
- package/dist/protected-BdnIEFkY.d.ts +0 -27
- package/dist/protected-xAr24FEC.d.ts +0 -40
- package/dist/relations-cjOL01hZ.js +0 -505
- package/dist/rls-P-BeBx0n.js +0 -238
- package/dist/rls-graph-CHwx0V8q.js +0 -435
- package/dist/rls-ownership-DcYlZ09B.js +0 -843
- package/dist/rls-shared-Cewd_-zI.js +0 -495
- package/dist/rls-verify-CMv9H3wV.js +0 -746
- package/dist/run-BWc9qqvE.js +0 -174
- package/dist/server-DlZ28Oz5.js +0 -268
- package/dist/sink-B1fZzIMf.d.ts +0 -104
- package/dist/sink-Bd9PSyfq.js +0 -141
- package/dist/skills-C5CbhhpG.js +0 -56
- package/dist/snapshot-Enxd0n-s.js +0 -975
- package/dist/sql-xdg5f4Fi.js +0 -27
- package/dist/src-BDwEZWrZ.js +0 -568
- package/dist/store-jOBYpiXC.d.ts +0 -36
- package/dist/stores-C0IMQrGI.d.ts +0 -52
- package/dist/subject-C6dLxqHU.js +0 -24
- package/dist/supabase-config-Bj3iDH2I.js +0 -472
- package/dist/supabase-hook-gd3s8bxB.js +0 -2
- package/dist/supabase-s4o9s1O1.js +0 -83
- package/dist/thenable-okc2xDsj.js +0 -10
- package/dist/types-BnIEz1sU.d.ts +0 -48
- package/dist/types-Bvc4oWT6.d.ts +0 -9
- package/dist/types-CKNSyIf_.d.ts +0 -52
- package/dist/types-Cq_H8N_X.d.ts +0 -197
- package/dist/usage-B0YuTQRO.js +0 -42
- package/dist/usage-DorAJiU_.js +0 -2
- package/dist/vocabulary-DA8a8zif.d.ts +0 -56
- package/dist/watch-BwxLTYMZ.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`, `
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
60
|
+
export const { approval, approvalFor, getPermDock } = createPermDock(policy, {
|
|
58
61
|
delegation: () => ({ scopes: [permissions.post.delete.scope] }),
|
|
59
|
-
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
190
|
+
webBotAuthVerifier,
|
|
187
191
|
} from "permdock/hono";
|
|
188
192
|
|
|
189
193
|
const keys = discoverViaSignatureAgent({ allow: ["agents.example.com"] });
|
|
190
194
|
|
|
191
|
-
export const {
|
|
195
|
+
export const { middleware, protect } = createPermDock(policy, {
|
|
192
196
|
subject: (c) => c.get("user"),
|
|
193
|
-
webBotAuth: (
|
|
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 {
|
|
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,
|
|
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:
|
|
45
|
-
escalation: { after:
|
|
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. **
|
|
59
|
-
✓
|
|
60
|
-
5. **
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
✓
|
|
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
|
|
80
|
-
-
|
|
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
|
|
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 /:
|
|
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 {
|
|
40
|
+
import { getPermDock, memoryApprovalPolicySource } from "permdock";
|
|
37
41
|
|
|
38
|
-
const permdock = await
|
|
39
|
-
approvalPolicies:
|
|
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
|
|
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
|
-
|
|
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`, `
|
|
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 `
|
|
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
|
|
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.
|
|
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.**
|
|
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.**
|
|
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
|
|