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,6 +1,6 @@
|
|
|
1
1
|
# Adapter factories
|
|
2
2
|
|
|
3
|
-
Every server adapter exports `createPermDock`. The import path names the framework. Agent runtime adapters (AI SDK, Claude Agent SDK, Eve, OpenAI Agents SDK, MCP, A2A, WebMCP, terminal) are in the `permdock-agents` skill; ORM adapters and Convex in the `permdock-data` skill. React has no factory: it exports hooks and `<Protected>` from `permdock/react`.
|
|
3
|
+
Every server adapter exports `createPermDock`. The import path names the framework. The implementation entries `permdock/resend`, `permdock/chat-sdk` and `permdock/workflow-sdk` export functions named after what they return, not a factory. Agent runtime adapters (AI SDK, Claude Agent SDK, Eve, OpenAI Agents SDK, MCP, A2A, WebMCP, terminal) are in the `permdock-agents` skill; ORM adapters and Convex in the `permdock-data` skill. React has no factory: it exports hooks and `<Protected>` from `permdock/react`.
|
|
4
4
|
|
|
5
5
|
## Next.js — `permdock/next`
|
|
6
6
|
|
|
@@ -8,12 +8,13 @@ File: `src/permdock/server.ts`
|
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import { createPermDock } from "permdock/next";
|
|
11
|
+
|
|
11
12
|
import { policy } from "../policy";
|
|
12
13
|
|
|
13
14
|
export const {
|
|
14
15
|
getPermDock,
|
|
15
16
|
getPermission,
|
|
16
|
-
|
|
17
|
+
requirePermission,
|
|
17
18
|
PermDockProvider,
|
|
18
19
|
permdockHandler,
|
|
19
20
|
} = createPermDock(policy, {
|
|
@@ -21,7 +22,7 @@ export const {
|
|
|
21
22
|
});
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
Needs Next.js 16.3 or later. Guard with `getPermission(permissions.post.update, post)` or `assert`. For a page or Server Action that must stop on a denial, `await
|
|
25
|
+
Needs Next.js 16.3 or later. Guard with `getPermission(permissions.post.update, post)` or `assert`. For a page or Server Action that must stop on a denial, `await requirePermission(permission, data, { tenant })` calls `forbidden()` (or `unauthorized()` for a signed-out user); enable `experimental.authInterrupts` and add `app/forbidden.tsx` and `app/unauthorized.tsx`. When only part of a page is gated, let a Server Component `assert` inside `<PermissionBoundary denied={...} approval={...}>` from `permdock/next/client` (with a `Suspense` inside it); `usePermissionBoundary()` in the fallback gives the permission, the approval `token` and `retry()`. Client components use `permdock/react` inside the server `PermDockProvider`, which never awaits: it streams a `snapshotPromise`, and permission hooks answer `pending` until it lands (add `suspend` to make them suspend).
|
|
25
26
|
|
|
26
27
|
With `cacheComponents`, when permission UI (nav items, row actions) must be prefetched, the app owns the cache directive and `getSnapshot` fills it. Without the factory, call `snapshotFor` and set `cacheLife(cacheLifeFor(snapshot))` and `cacheTag(snapshotTag(sub))` yourself. Never put `'use cache'` inside PermDock calls, and never read `headers()` / `cookies()` in a function you mean to cache outside `'use cache: private'`:
|
|
27
28
|
|
|
@@ -41,18 +42,18 @@ export async function loadSnapshot(org: string) {
|
|
|
41
42
|
|
|
42
43
|
Hooks under a `snapshotPromise` never suspend: they deny with `status: 'pending'` until it resolves, so render a skeleton for `pending` (or `<Protected pending>`); pass `suspend` to the provider only when readers should wait inside a Suspense boundary.
|
|
43
44
|
|
|
44
|
-
After a role change: `updateTag(snapshotTag(user))` in the Server Action; `revalidateTag(tag, { expire: 0 })` in a Route Handler (not `'max'`, which would keep serving the revoked grant). Pages whose permission UI must be instant export `instant = true`; a rarely visited admin page exports `prefetch = 'force-disabled'`. Resource links that should carry their gated actions use `<Link prefetch={true}>` with the check in a `'use cache: private'` function keyed on the resource id. In `proxy.ts`, use `mayAccess(policy, claims, permission, { tenant })` (optimistic, never a decision). Both reach only the acting browser; for other members, add an app-owned signal (Realtime, poll, SSE) that calls `router.refresh()`. Keep the `[org]` layout synchronous and never read `cookies()` outside the private-cached loader. With a slug in the URL (`[orgSlug]`), resolve it to the org id in a `'use cache'` lookup that reads no session, call `notFound()` for an unknown slug, and pass the id to `snapshotFor`, `
|
|
45
|
+
After a role change: `updateTag(snapshotTag(user))` in the Server Action; `revalidateTag(tag, { expire: 0 })` in a Route Handler (not `'max'`, which would keep serving the revoked grant). Pages whose permission UI must be instant export `instant = true`; a rarely visited admin page exports `prefetch = 'force-disabled'`. Resource links that should carry their gated actions use `<Link prefetch={true}>` with the check in a `'use cache: private'` function keyed on the resource id. In `proxy.ts`, use `mayAccess(policy, claims, permission, { tenant })` (optimistic, never a decision). Both reach only the acting browser; for other members, add an app-owned signal (Realtime, poll, SSE) that calls `router.refresh()`. Keep the `[org]` layout synchronous and never read `cookies()` outside the private-cached loader. With a slug in the URL (`[orgSlug]`), resolve it to the org id in a `'use cache'` lookup that reads no session, call `notFound()` for an unknown slug, and pass the id to `snapshotFor`, `requirePermission` and `getPermDock`. Export `const { POST, GET } = permdockHandler()` from `app/api/permdock/route.ts`, or pass `endpoint: false` when every client check is portable; then a closure grant read by `usePermission` is denied with reason `server-only` (`permdock doctor` PD044 warns). Guide: [Next.js Cache Components](https://permdock.com/docs/guides/next-cache-components).
|
|
45
46
|
|
|
46
47
|
## Hono — `permdock/hono`
|
|
47
48
|
|
|
48
49
|
```ts
|
|
49
50
|
import { createPermDock } from "permdock/hono";
|
|
50
51
|
|
|
51
|
-
export const {
|
|
52
|
+
export const { middleware, protect, permdockHandler } = createPermDock(policy, {
|
|
52
53
|
subject: (c) => c.get("user"),
|
|
53
54
|
});
|
|
54
55
|
|
|
55
|
-
app.use("*",
|
|
56
|
+
app.use("*", middleware());
|
|
56
57
|
app.delete(
|
|
57
58
|
"/posts/:id",
|
|
58
59
|
protect(permissions.post.delete, (c) => loadPost(c)),
|
|
@@ -60,7 +61,7 @@ app.delete(
|
|
|
60
61
|
);
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
Every HTTP adapter takes `tenant` (for example `(c) => c.req.param('org')`), resolved again on each `protect` where route params exist, plus `limits` (a `LimitStore
|
|
64
|
+
Every HTTP adapter takes `tenant` (for example `(c) => c.req.param('org')`), resolved again on each `protect` where route params exist, plus `limits` (`limits(store)` over a `LimitStore`, for quota grants) and `pdp` (`getPermDock` from `permdock/pdp`). A `protect` loader's result is validated against the resource schema; pass `{ trusted: true }` as the third argument only when the loader returns a row the server loaded itself. `assert` inside a handler becomes the same 403 Problem Details as a guard denial; no `onError` wiring is needed.
|
|
64
65
|
|
|
65
66
|
Streams and sockets: pass `revocations: memoryRevocationFeed()` (from `permdock`) and open a connection after `protect` succeeded. `sse` drops items the subscriber cannot read and ends with an `event: permdock` frame on revocation; await it last. `socket` closes a WebSocket with `1008`. Check inbound socket messages with `conn.check(permission, data)`, which validates the message data; a denial keeps the socket open.
|
|
66
67
|
|
|
@@ -84,7 +85,7 @@ app.get(
|
|
|
84
85
|
|
|
85
86
|
Publish `revocations.revoke({ principal, tenant, kind: 'changed' })` from your role-edit code so open connections revalidate.
|
|
86
87
|
|
|
87
|
-
For a
|
|
88
|
+
For a React Router loader, `createPermDock` from `permdock/server` returns `getSnapshot(request, { tenant })`: the snapshot a client provider takes, built from the request's own subject. Send it with `snapshotHeaders(snapshot)` from `permdock/server`, which sets a private `Cache-Control` from the snapshot's expiry. Docs: [loader snapshots](https://permdock.com/docs/adapters/server-kernel).
|
|
88
89
|
|
|
89
90
|
## React (Vite) — `permdock/react`
|
|
90
91
|
|
|
@@ -104,12 +105,12 @@ import { createPermDock } from "permdock/authzen";
|
|
|
104
105
|
export const { permdockHandler } = createPermDock(policy, {
|
|
105
106
|
subject: fromBearer,
|
|
106
107
|
resources: {
|
|
107
|
-
post: {
|
|
108
|
+
post: { data: (id) => loadPost(id), list: () => listPosts() },
|
|
108
109
|
},
|
|
109
110
|
});
|
|
110
111
|
```
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
`permdockHandler()` returns one Fetch handler that serves `POST /access/v1/evaluation`, `/evaluations`, `/search/action`, `/search/resource`, `/search/subject` and `GET /.well-known/authzen-configuration`. PEP authentication is required (`401` unless `anonymous: true`). The body `subject` is the evaluation principal when the PEP is trusted (default). Unknown actions return `decision: false` with `context.reason: 'unknown-permission'`. Omit `subjects.list` to drop search/subject from discovery. A custom PDP that evaluates delegated callers outside `createPermDock` calls `coveredByDelegation(permission, delegation, resourceId, hasActor)` from `permdock` instead of re-implementing scope, `authorization_details` and GNAP `access` matching.
|
|
113
114
|
|
|
114
115
|
## OpenAPI — `permdock/openapi`
|
|
115
116
|
|
|
@@ -138,18 +139,18 @@ import { PermDockProvider, usePermission } from "permdock/react-native";
|
|
|
138
139
|
|
|
139
140
|
To answer guards from synced rows, write `localSnapshotManifest(policy)` (from `permdock`) to a JSON file at build time and pass `source={localSnapshot({ manifest, read, subscribe })}`. `read()` returns `{ principal: { id, tenant, roles, memberships, attributes }, customRoles }` from the local database; `subscribe` re-reads on row changes. It decides like the server snapshot except relation grantees (server-only) and the assignable lists (empty). Never import `policy.ts` into the app for this. Docs: [local snapshot](https://permdock.com/docs/adapters/react-native#local-snapshot).
|
|
140
141
|
|
|
141
|
-
For an offline app on PowerSync, set `powersync: { out: 'sync-config.yaml' }` in `permdock.config.ts` and run `permdock powersync generate`: one edition 3 Sync Stream per resource, from the `rls.tables` and `rls.memberships` the RLS generator reads. It under-syncs, never over-syncs: a resource with a deny grant gets no stream, and a grant with a global role, approval, break-glass, validity, request context or a condition without a Sync Streams form syncs nothing, each with a warning. The app reads what does not sync from the server. It also writes `permdock_*` streams for the user's own membership, global-role and custom-role rows; with `powersync.manifest` set it writes the manifest too, and `
|
|
142
|
+
For an offline app on PowerSync, set `powersync: { out: 'sync-config.yaml' }` in `permdock.config.ts` and run `permdock powersync generate`: one edition 3 Sync Stream per resource, from the `rls.tables` and `rls.memberships` the RLS generator reads. It under-syncs, never over-syncs: a resource with a deny grant gets no stream, and a grant with a global role, approval, break-glass, validity, request context or a condition without a Sync Streams form syncs nothing, each with a warning. The app reads what does not sync from the server. It also writes `permdock_*` streams for the user's own membership, global-role and custom-role rows; with `powersync.manifest` set it writes the manifest too, and `powerSyncSource(db, { manifest, queries, read })` from `permdock/react-native` builds the local snapshot from `db.watch` queries. `permdock powersync verify` fails when a stream holds a fixture row the policy denies; doctor PD058 flags a stale file or manifest. Doctor PD065 lists server-only grants read from React Native code, which the device denies offline. Docs: [`permdock powersync`](https://permdock.com/docs/cli/powersync).
|
|
142
143
|
|
|
143
144
|
## Express — `permdock/express`
|
|
144
145
|
|
|
145
146
|
```ts
|
|
146
147
|
import { createPermDock } from "permdock/express";
|
|
147
148
|
|
|
148
|
-
export const {
|
|
149
|
+
export const { middleware, protect, errorHandler } = createPermDock(policy, {
|
|
149
150
|
subject: (req) => req.user ?? null,
|
|
150
151
|
});
|
|
151
152
|
|
|
152
|
-
app.use(
|
|
153
|
+
app.use(middleware());
|
|
153
154
|
app.delete(
|
|
154
155
|
"/posts/:id",
|
|
155
156
|
protect(permissions.post.delete, (req) => loadPost(req.params.id)),
|
|
@@ -158,18 +159,18 @@ app.delete(
|
|
|
158
159
|
app.use(errorHandler());
|
|
159
160
|
```
|
|
160
161
|
|
|
161
|
-
Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. A PermDock error thrown by `
|
|
162
|
+
Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. A PermDock error thrown by `middleware()`, `protect` or the decision routes becomes Problem Details; any other error goes to `next(err)`.
|
|
162
163
|
|
|
163
164
|
## Fastify — `permdock/fastify`
|
|
164
165
|
|
|
165
166
|
```ts
|
|
166
167
|
import { createPermDock } from "permdock/fastify";
|
|
167
168
|
|
|
168
|
-
export const {
|
|
169
|
+
export const { middleware, protect } = createPermDock(policy, {
|
|
169
170
|
subject: (request) => request.user ?? null,
|
|
170
171
|
});
|
|
171
172
|
|
|
172
|
-
await app.register(
|
|
173
|
+
await app.register(middleware());
|
|
173
174
|
app.delete(
|
|
174
175
|
"/posts/:id",
|
|
175
176
|
{
|
|
@@ -188,11 +189,11 @@ Registers a `fastify-plugin`-style root plugin (`skip-override`) that decorates
|
|
|
188
189
|
```ts
|
|
189
190
|
import { createPermDock } from "permdock/elysia";
|
|
190
191
|
|
|
191
|
-
export const {
|
|
192
|
+
export const { middleware, protect } = createPermDock(policy, {
|
|
192
193
|
subject: ({ store }) => store.user ?? null,
|
|
193
194
|
});
|
|
194
195
|
|
|
195
|
-
const app = new Elysia().use(
|
|
196
|
+
const app = new Elysia().use(middleware()).delete("/posts/:id", handler, {
|
|
196
197
|
beforeHandle: protect(permissions.post.delete, ({ params }) =>
|
|
197
198
|
loadPost(params.id),
|
|
198
199
|
),
|
|
@@ -201,6 +202,59 @@ const app = new Elysia().use(permdock()).delete("/posts/:id", handler, {
|
|
|
201
202
|
|
|
202
203
|
Fetch-native plugin via `derive`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. For `.ws` routes, `connection(ws, { permission })` in `open` returns the socket's connection (memoised); it closes the socket with `1008` when revoked.
|
|
203
204
|
|
|
205
|
+
## Meta-framework servers — `permdock/sveltekit`, `permdock/astro`, `permdock/solid-start`, `permdock/tanstack-start`, `permdock/h3`
|
|
206
|
+
|
|
207
|
+
Each `createPermDock` returns `{ middleware, protect, getPermDock, getSnapshot, connection, permdockHandler, openapi }`; all but TanStack Start add `withPermDock(handler)`, which types the instance for a handler without a check and answers a thrown PermDock error with Problem Details. `protect(target, data?, options?)` takes a permission, an operation from `defineOperations` or `null`; a loader that returns `null` or `undefined` answers `404`. The kernel caches one instance per request and tenant, so `middleware()` plus `protect` builds one. Nitro and Nuxt server routes are h3 handlers and use `permdock/h3`; there is no `permdock/nitro` or `permdock/nuxt`.
|
|
208
|
+
|
|
209
|
+
| Entry | `subject` receives | `middleware()` goes in | Instance and row land on | Docs |
|
|
210
|
+
| ------------------------- | ------------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
211
|
+
| `permdock/sveltekit` | `RequestEvent` | `export const handle` in `src/hooks.server.ts` | `event.locals.permdock`, `event.locals.permdockData` | [SvelteKit](https://permdock.com/docs/adapters/sveltekit) |
|
|
212
|
+
| `permdock/astro` | `APIContext` | `export const onRequest` in `src/middleware.ts` | `context.locals.permdock`, `context.locals.permdockData` | [Astro](https://permdock.com/docs/adapters/astro) |
|
|
213
|
+
| `permdock/solid-start` | the h3 event under the request | `createMiddleware([middleware()])` in `src/middleware.ts` | `event.context.permdock` (middleware); `event.locals.permdock`, `permdockData` (API routes) | [SolidStart](https://permdock.com/docs/adapters/solid-start) |
|
|
214
|
+
| `permdock/tanstack-start` | the Fetch `Request` | `requestMiddleware` in `createStart` | middleware `context.permdock`, `context.permdockData` | [TanStack Start](https://permdock.com/docs/adapters/tanstack-start) |
|
|
215
|
+
| `permdock/h3` | `H3Event` | `app.use(middleware())` | `event.context.permdock`, `event.context.permdockData` | [h3](https://permdock.com/docs/adapters/h3) |
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// SvelteKit: src/hooks.server.ts, then src/routes/api/posts/[id]/+server.ts
|
|
219
|
+
export const handle = middleware();
|
|
220
|
+
export const PATCH = protect(permissions.post.update, (event) =>
|
|
221
|
+
loadPost(event.params.id),
|
|
222
|
+
)((event) => Response.json(event.locals.permdockData));
|
|
223
|
+
|
|
224
|
+
// Astro: src/middleware.ts; endpoints wrap like SvelteKit with context.locals
|
|
225
|
+
export const onRequest = middleware();
|
|
226
|
+
|
|
227
|
+
// SolidStart: src/middleware.ts, registered with solidStart({ middleware: "./src/middleware.ts" })
|
|
228
|
+
export default createMiddleware([middleware()]);
|
|
229
|
+
|
|
230
|
+
// TanStack Start: src/start.ts, then a server route
|
|
231
|
+
export const startInstance = createStart(() => ({
|
|
232
|
+
requestMiddleware: [middleware()],
|
|
233
|
+
}));
|
|
234
|
+
export const Route = createFileRoute("/api/posts/$id")({
|
|
235
|
+
server: {
|
|
236
|
+
middleware: [protect(permissions.post.update, postFromPath)],
|
|
237
|
+
handlers: { PATCH: ({ context }) => Response.json(context.permdockData) },
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// h3 (Nitro, Nuxt server routes)
|
|
242
|
+
app.use(middleware());
|
|
243
|
+
app.patch(
|
|
244
|
+
"/posts/:id",
|
|
245
|
+
withPermDock<Post>((event) => update(event.context.permdockData)),
|
|
246
|
+
{
|
|
247
|
+
middleware: [
|
|
248
|
+
protect(permissions.post.update, (event) =>
|
|
249
|
+
loadPost(event.context.params?.id),
|
|
250
|
+
),
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The decision endpoint is `export const { GET, POST } = permdockHandler()` (SvelteKit, Astro, SolidStart), `server: { handlers: permdockHandler() }` (TanStack Start) or `app.all("/api/permdock", permdockHandler())` (h3). Pages and forms call `getPermDock` and check with `can` or `assert`: a SvelteKit form action or `load` maps a thrown PermDock error to `error(403)`, an Astro page calls `getPermDock(Astro)` and redirects, a SolidStart server function calls `getPermDock(getRequestEvent())`, and a TanStack Start server function calls `getPermDock(getRequest())`. TanStack Start route params are not on the `Request`; parse them from `request.url`. Route `beforeLoad` and `loader` also run on the client, so they are never the guard. Astro needs `output: "server"` or `prerender = false` on the guarded route. `App.Locals` augmentation is left to the app.
|
|
257
|
+
|
|
204
258
|
## Nest — `permdock/nest`
|
|
205
259
|
|
|
206
260
|
```ts
|
|
@@ -222,7 +276,7 @@ export class AppModule {}
|
|
|
222
276
|
```ts
|
|
223
277
|
import { createPermDock } from "permdock/node";
|
|
224
278
|
|
|
225
|
-
export const {
|
|
279
|
+
export const { getPermDock, protect, send, permdockHandler } = createPermDock(
|
|
226
280
|
policy,
|
|
227
281
|
{
|
|
228
282
|
subject: (req) => userFromCookie(req.headers.cookie),
|
|
@@ -230,36 +284,36 @@ export const { permdock, protect, send, permdockHandler } = createPermDock(
|
|
|
230
284
|
);
|
|
231
285
|
```
|
|
232
286
|
|
|
233
|
-
Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. Express and Nest reuse `toRequest` / `
|
|
287
|
+
Converts `IncomingMessage` to Fetch, then delegates to `permdock/server`. Denials are `403 application/problem+json`; anonymous callers get `401` plus `WWW-Authenticate`. Express and Nest reuse `toRequest` / `sendResponse`. The body is read only when consumed, so a body parser after `getPermDock(req)` still works. There is no error hook: wrap handlers that call `assert` and `send(res, problemFromError(error))` (from `permdock/server`) when it returns a `Response`.
|
|
234
288
|
|
|
235
289
|
## tRPC — `permdock/trpc`
|
|
236
290
|
|
|
237
291
|
```ts
|
|
238
292
|
import { createPermDock } from "permdock/trpc";
|
|
239
293
|
|
|
240
|
-
export const {
|
|
294
|
+
export const { middleware, protect, permdockHandler } = createPermDock(policy, {
|
|
241
295
|
subject: (opts) => opts.ctx.user ?? null,
|
|
242
296
|
});
|
|
243
297
|
|
|
244
|
-
const procedure = t.procedure.use(
|
|
298
|
+
const procedure = t.procedure.use(middleware());
|
|
245
299
|
procedure
|
|
246
300
|
.input(z.object({ id: z.string() }))
|
|
247
301
|
.use(protect(permissions.post.delete, ({ input }) => loadPost(input.id)))
|
|
248
302
|
.mutation(({ ctx }) => deletePost(ctx.permdockData));
|
|
249
303
|
```
|
|
250
304
|
|
|
251
|
-
`protect` throws `TRPCError` (`FORBIDDEN`, `BAD_REQUEST`, `UNAUTHORIZED`) with Problem Details as `cause`; pass `errorFormatter` to `initTRPC.create` so clients see it under `data`. `ctx.permdock.assert` in a resolver maps to the same error. Read the tenant from each procedure's input (`tenant: (opts) => opts.input?.org`) so a batch never shares one; `request: (ctx) => ctx.req` names the Web `Request` when the context holds it elsewhere. `openapi.security(permission)` is the `trpc-to-openapi` meta fragment. On a subscription, `protect(permission, load, { items })` ends the iterator with `UNAUTHORIZED` or `FORBIDDEN` when the session is revoked or the permission re-denied, and drops items the subscriber cannot read; pass `revocations`.
|
|
305
|
+
`protect` throws `TRPCError` (`FORBIDDEN`, `BAD_REQUEST`, `UNAUTHORIZED`) with Problem Details as `cause`; pass `errorFormatter` to `initTRPC.create` so clients see it under `data`. `ctx.permdock.assert` in a resolver maps to the same error. Read the tenant from each procedure's input (`tenant: (opts) => opts.input?.org`) so a batch never shares one; `request: (ctx) => ctx.req` names the Web `Request` when the context holds it elsewhere. `permdockHandler()` returns the decision endpoint as a Fetch handler. `openapi.security(permission)` is the `trpc-to-openapi` meta fragment. On a subscription, `protect(permission, load, { items })` ends the iterator with `UNAUTHORIZED` or `FORBIDDEN` when the session is revoked or the permission re-denied, and drops items the subscriber cannot read; pass `revocations`.
|
|
252
306
|
|
|
253
307
|
## oRPC — `permdock/orpc`
|
|
254
308
|
|
|
255
309
|
```ts
|
|
256
310
|
import { createPermDock } from "permdock/orpc";
|
|
257
311
|
|
|
258
|
-
export const {
|
|
312
|
+
export const { middleware, protect, permdockHandler } = createPermDock(policy, {
|
|
259
313
|
subject: ({ context }) => context.user ?? null,
|
|
260
314
|
});
|
|
261
315
|
|
|
262
|
-
const base = os.$context<Context>().use(
|
|
316
|
+
const base = os.$context<Context>().use(middleware());
|
|
263
317
|
base
|
|
264
318
|
.input(z.object({ id: z.string() }))
|
|
265
319
|
.use(protect(permissions.post.delete, ({ input }) => loadPost(input.id)))
|
|
@@ -313,6 +367,14 @@ instrument(permdock, {
|
|
|
313
367
|
|
|
314
368
|
HTTP adapters accept the same options as `otel`. `@opentelemetry/api` is optional; without it the adapter writes only through `logger`.
|
|
315
369
|
|
|
370
|
+
## Adapter hooks
|
|
371
|
+
|
|
372
|
+
Every HTTP adapter, `permdock/mcp` and `permdock/ai-sdk` take three hooks ([extend PermDock](https://permdock.com/docs/guides/extending)):
|
|
373
|
+
|
|
374
|
+
- `deniedResponse`: on HTTP, `({ decision, problem, request })` returns Problem Details with extra members, a `Response`, or `undefined`; a status below 300 or a throw keeps the default. On MCP and the agent adapters, `deniedText` returns replacement text only. On Next.js the factory `onDenied` throws or redirects, and also runs for `requirePermission`.
|
|
375
|
+
- `context`: returns plain JSON merged into `subject.context`. Return only server-derived values, never a header, body field or tool argument; it is visible in the snapshot.
|
|
376
|
+
- `wrap`: wraps each instance after `otel`; `permdock/next` takes it too. Build it with `wrapPermDock(permdock, (inner) => ({ … }))` so `tenant()`, `team()` and `derive()` stay wrapped.
|
|
377
|
+
|
|
316
378
|
## SCIM — `permdock/scim`
|
|
317
379
|
|
|
318
380
|
No factory. Mount `scimHandler` and pass `directoryMembershipSource(store)` as `memberships`:
|
|
@@ -327,7 +389,7 @@ import {
|
|
|
327
389
|
|
|
328
390
|
const directory = memoryDirectoryStore();
|
|
329
391
|
export const scim = scimHandler({
|
|
330
|
-
|
|
392
|
+
directory,
|
|
331
393
|
tenant: (request) => tenantFromPath(request),
|
|
332
394
|
token: { hash: "sha256", lookup: (tenant) => hashFor(tenant) },
|
|
333
395
|
});
|
|
@@ -337,7 +399,7 @@ The handler writes users and groups. It never decides. Unknown or non-assignable
|
|
|
337
399
|
|
|
338
400
|
## Cloud — `permdock/cloud`
|
|
339
401
|
|
|
340
|
-
No factory for a `PermDock`. `cloud({ url, key, environment })` returns `approvals`, `sink
|
|
402
|
+
No factory for a `PermDock`. `cloud({ url, key, environment })` returns `approvals`, `sink` and `policies` to pass into any adapter, `snapshotsFor(token)` (a `SnapshotSource` for the subject an `at+jwt` proves, sent as `PermDock-Subject`), `catalog.push`, plus the environment `issuer` (`<url>/v1/environments/<environment>`) and its `jwks` URL; `cloudEndpoints({ url, environment })` computes the same two without a key. It never implements `MembershipSource` or `CustomRoleSource` and never decides.
|
|
341
403
|
|
|
342
404
|
```ts
|
|
343
405
|
import { cloud } from "permdock/cloud";
|
|
@@ -354,22 +416,49 @@ export const { getPermDock } = createPermDock(policy, {
|
|
|
354
416
|
});
|
|
355
417
|
```
|
|
356
418
|
|
|
357
|
-
Hosted grants are opt-in per permission. List the permissions a Cloud admin may grant in `definePolicy(..., { hostable: [permissions.auditLog.read] })`, pass `verifier: joseTokenVerifier({ jwks: cloudEndpoints({ url, environment }).jwks })` to `cloud()` (the policy document's issuer and audience are both the environment URL), forward `policies: permdockCloud.policies`, and call `permdockCloud.policies.refresh()` on a timer. Never mark a permission `hostable` when `permdock rls` compiles its table (`permdock doctor` PD020).
|
|
419
|
+
Hosted grants are opt-in per permission. List the permissions a Cloud admin may grant in `definePolicy(..., { hostable: [permissions.auditLog.read] })`, pass `verifier: joseTokenVerifier({ jwks: cloudEndpoints({ url, environment }).jwks })` to `cloud()` (the policy document's issuer and audience are both the environment URL), forward `policies: hostedPolicies(permdockCloud.policies)`, and call `permdockCloud.policies.refresh()` on a timer. Never mark a permission `hostable` when `permdock rls` compiles its table (`permdock doctor` PD020).
|
|
420
|
+
|
|
421
|
+
An app that calls `permdockCloud.approvals.resolve` also passes `approver: { signer }`, a `TokenSigner` over the app's own key whose JWK Set the Cloud environment trusts. Each resolve then carries a `PermDock-Approver` RFC 9068 `at+jwt` for `verdict.by.principal.id`. Without it, `resolve` throws `approver-unauthenticated`, and a verdict with an actor throws `approver-not-eligible`. The token carries the approver's `roles`, `memberships` and `tenant`; pass `approver: { signer, permissions, relations }` (typically from `approverPermissions` and `approverRelations`) when a request's `approvers` names a `holder()` or `relation()`, because a fact the token lacks counts as not held. Never pass `cloudSigner` here: it refuses `at+jwt` ([approver token](https://permdock.com/docs/adapters/cloud#approver-token)).
|
|
358
422
|
|
|
359
|
-
`PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` are server-only. Production `PERMDOCK_CLOUD_URL` is `https://
|
|
423
|
+
`PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` are server-only. Production `PERMDOCK_CLOUD_URL` is `https://permdock.com/api`, which is also the platform issuer `permdock login` signs in at. The dashboard is `https://app.permdock.com`; the read-only MCP server is `https://permdock.com/mcp`. `PERMDOCK_CLOUD_TOKEN` is a read-only CI key for `permdock cloud pull` and `permdock rls generate --project`; never deploy it with the app. A Cloud outage leaves directory memberships at their last synced state.
|
|
424
|
+
|
|
425
|
+
## Delivery, chat and automation implementations — `permdock/resend`, `permdock/chat-sdk`, `permdock/workflow-sdk`
|
|
426
|
+
|
|
427
|
+
These entries fill the feature contracts `permdock/delivery`, `permdock/chat` and `permdock/workflow` with one vendor SDK each. None imports the SDK at runtime: pass in your own client or functions (`resend` 6, `chat` 4 and `workflow` are optional peers). Automations and chat bots are in the `permdock-agents` skill and approval prompts in the `permdock-approvals` skill; install each with `npx skills add ScaleDockHQ/PermDock --skill <name>`.
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
import { chatSdkTransport } from "permdock/chat-sdk";
|
|
431
|
+
import { createPermDock } from "permdock/delivery";
|
|
432
|
+
import { resendTransport } from "permdock/resend";
|
|
433
|
+
|
|
434
|
+
const delivery = createPermDock(policy, {
|
|
435
|
+
transports: {
|
|
436
|
+
email: resendTransport(new Resend(process.env.RESEND_API_KEY), {
|
|
437
|
+
from: "Acme <billing@acme.test>",
|
|
438
|
+
}),
|
|
439
|
+
chat: chatSdkTransport(chat, { provider: "slack", workspace: "T0ACME" }),
|
|
440
|
+
},
|
|
441
|
+
principals,
|
|
442
|
+
sink,
|
|
443
|
+
});
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
- **`permdock/resend`** ([Resend](https://permdock.com/docs/adapters/resend)): `resendTransport(resend, { from?, replyTo?, tags? })` is the email transport; it sends each recipient's idempotency key as Resend's `idempotencyKey`, fans out through `batch.send` and maps Resend errors to retryable or final failures (`resendFailure`). `approvalEmail(prompt, { approvalUrl, product? })` renders an approval prompt that links once to the approval page and has no approve or reject link. `resendWebhook({ resend, secret, sink, replay })` is a fetch handler that verifies the signature, claims each webhook id in a `ReplayStore` (`memoryReplayStore` from `permdock/server`) and writes delivered, bounced, complained, failed and suppressed events to the sink.
|
|
447
|
+
- **`permdock/chat-sdk`** ([Chat SDK](https://permdock.com/docs/adapters/chat-sdk)): `chatSdkAuthor(user, adapter, { workspace? })` turns a verified Chat SDK `Author` into a `ChatAuthor` from `userId` only, and returns `null` for bots and the platform. `chatSdkTransport(chat, { provider, workspace?, remember? })` posts direct messages, and `approvalCard(prompt, { approvalUrl, buttons? })` posts the approval prompt with a Review link and buttons that carry the request id, never the token. `approvalActions(permdock, { workspace?, onResult? })` applies those clicks as the clicking user through `ChatPermDock.resolveAction`: `chat.onAction([...actions.actionIds], actions.onAction)`.
|
|
448
|
+
- **`permdock/workflow-sdk`** ([Workflow SDK](https://permdock.com/docs/adapters/workflow-sdk)): `startAutomation(workflow, start, run, id, args, options?)` starts a Workflow SDK run only after `WorkflowPermDock.start` grants it, with the run ticket as the first argument. `workflowSdkWaiter({ createHook, resumeHook, sleep? })` is an `ApprovalWaiter` that parks a run on the hook `approvalHookToken(token)` until `waiter.wake(token)`. `authorizeHook(resumeHook, caller, permission, row, token, payload)` resumes a hook only when `caller` is granted the permission; a gone hook denies with `validation` (`cause: 'hook-unavailable'`).
|
|
360
449
|
|
|
361
450
|
## Better Auth — `permdock/better-auth`
|
|
362
451
|
|
|
363
452
|
```ts
|
|
364
453
|
import {
|
|
365
454
|
subjectFromBetterAuth,
|
|
366
|
-
|
|
455
|
+
betterAuthCustomRoleSource,
|
|
367
456
|
} from "permdock/better-auth";
|
|
368
457
|
|
|
369
458
|
const session = await auth.api.getSession({ headers });
|
|
370
|
-
const subject = await subjectFromBetterAuth(
|
|
371
|
-
const permdock = await
|
|
372
|
-
customRoles:
|
|
459
|
+
const subject = await subjectFromBetterAuth(session, { auth });
|
|
460
|
+
const permdock = await getPermDock(policy, subject, {
|
|
461
|
+
customRoles: betterAuthCustomRoleSource(auth),
|
|
373
462
|
});
|
|
374
463
|
```
|
|
375
464
|
|
|
@@ -385,7 +474,7 @@ export const { getPermDock } = createPermDock(policy, {
|
|
|
385
474
|
});
|
|
386
475
|
```
|
|
387
476
|
|
|
388
|
-
Pass `auth()` or a verified session payload only. A plain `{ userId }` object is anonymous. `memberships: 'all'` loads organizations through the Clerk Backend API; create `
|
|
477
|
+
Pass `auth()` or a verified session payload only. A plain `{ userId }` object is anonymous. `memberships: 'all'` loads organizations through the Clerk Backend API; create `clerkSubjectResolver({ memberships: 'all', backend, cache: { ttl: 10 } })` once per process to cache each user's list (at most 30 seconds, so a removed membership grants until then). `o:` plans and features from `pla` and `fea` hold only in the session organization.
|
|
389
478
|
|
|
390
479
|
## Supabase — `permdock/supabase`
|
|
391
480
|
|
|
@@ -393,16 +482,20 @@ Pass `auth()` or a verified session payload only. A plain `{ userId }` object is
|
|
|
393
482
|
import { subjectFromSupabase, authorizeSql } from "permdock/supabase";
|
|
394
483
|
|
|
395
484
|
const subject = subjectFromSupabase(claims, {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
485
|
+
claims: {
|
|
486
|
+
roles: "user_role",
|
|
487
|
+
tenant: "tenant_id",
|
|
488
|
+
memberships: "memberships",
|
|
489
|
+
},
|
|
399
490
|
declared: ["member", "admin"],
|
|
400
491
|
});
|
|
401
492
|
```
|
|
402
493
|
|
|
403
|
-
Write the claims with `permdock supabase hook generate`; `permdock supabase inspect --
|
|
494
|
+
Write the claims with `permdock supabase hook generate`; `permdock supabase inspect --out` writes the manifest (helper names, tenant claim, budget, claims written) that `authorizationProvider` from `permdock/better-supabase` turns into better-supabase's `authorization` config. No `@supabase/supabase-js` peer. Pair with `permdock rls generate`, or keep SQL as authority and run `permdock rls verify --db-url-stdin` against `sqlFunction` twins ([database-first](https://permdock.com/docs/adapters/rls)); the generic RLS workflow is in the `permdock-data` skill.
|
|
495
|
+
|
|
496
|
+
With better-supabase, set `authorization: authorizationProvider({ manifest, catalog })` in `better-supabase.config.ts` (with `rls.customRoles` its `canAssign` and `canAssignFor` call `permdock_can_assign_any` and its `_for` form, so custom roles are assignable through better-supabase; regenerate the manifest after turning custom roles on), pass `authorizer({ policy })` to the server (`createNext`, `createHono`, `createMcp`) so every `permission` a resource, action, route guard or tool declares is decided by PermDock from the verified claims (`operations` maps tables for `permissions: true`; a missing or anonymous claim denies), build bucket and topic `access` policies with `bucketPolicy` and `topicPolicy`, and map its session with `subjectFromBetterSupabase(session)` (all from `permdock/better-supabase`): the active tenant's `features` become `principal.plans`. Pass `plans: { keys }` when better-supabase's `entitlements.claim.keys` shortens feature keys, and `apiKeys: { permissions, manifest }` so an `apiKey` session maps to the key's credential subject instead of the anonymous one. For `createMcp` tools, put the permission in `meta` and spread `toolPolicy({ permdock })` into the options: tools without one are hidden and refused, and approval-required calls are refused because `createMcp` collects no approvals. Wrap a `CredentialProvider` with `credentialGuard(provider, { permdock, use, revoke? })` so PermDock decides each token use. `authorizationProvider({ ..., approver })` lets holders of that permission approve AI tool calls, never their own. `approvers: { 'ai-tool': holder(p), 'support-session': ..., 'invoice.send': ... }` fills `isApprover` through `permdock_is_approver` (needs `rls.approvals`); only `user()`, `holder()` and `anyOf()` approvers, and a kind it does not name has no approver. `accessReviewApplier({ client, reviews, policy, demoteTo?, email?, permdockFor? })` applies a review's revoked items: `update_member_role` to the `demoteTo` role when that role lacks the permission, else `remove_member`, plus `revoke_invitation` for the member's matching invitations, then `markApplied`; pass the reviewer's client so the blocks' own checks run. better-supabase's `createAgentRuntime({ toolApproval })` takes `toolApproval` from `permdock/ai-sdk` as it is. Validate its claims with `betterSupabase.claims(supabaseClaims().extend(appSchema))` (`supabaseClaims` from `permdock/supabase`, any Standard Schema as `appSchema`) instead of a hand-written copy of the claim shape. A `supabase.hook.claims` function that needs the user's organizations calls `<schema>.member_organization_ids_for(user_id)` (generated by `rls generate`, executable by `supabase_auth_admin` only) instead of querying the membership tables itself. A token Supabase's OAuth server issued to a third-party app (`client_id`) becomes an `oauth-client` actor limited to its `scope`; `deny(permission, { to: actor('oauth-client') })` keeps such apps out, in the app and, through `rls generate`, in Postgres. A better-supabase support session (`act.kind: 'support'`) is actor kind `support` with `sessionId` and `readOnly`, and `actingAs` (`act.kind: 'impersonation'`) is kind `impersonation`; neither has a delegation, so they reach nothing until a policy delegation names `actor('support')` or `actor('impersonation')`, and `readOnly: true` (also when `read_only` is absent) narrows that to read-only permissions. Generated RLS reads memberships, not `act`, so a support token sent to the Data API writes as the user: add a restrictive `for update` (and `insert` / `delete`) policy per writable table that requires `act ->> 'read_only' = 'false'` when `act` is a support or session level ([support access](https://permdock.com/docs/guides/support-access)). With `rls.anonymousSignIns: 'deny'`, pass `anonymousSignIns: 'deny'` to the subject mapper too (doctor PD057). Check a deployed database against the generated policies with `permdock rls verify --introspect`.
|
|
404
497
|
|
|
405
|
-
|
|
498
|
+
The other better-supabase slots, all from `permdock/better-supabase` ([better-supabase](https://permdock.com/docs/adapters/better-supabase)): `hookCan(permdock)` and `handlerCan({ getPermDock })` are the `can` options of `authorizeHook` and `protectWebHandler` in `better-supabase/workflow-sdk`; they decide with no row, so a permission with a row condition denies there and is checked inside the handler, and `handlerCan` answers `undefined` (401) without a signed-in principal. `supportPolicy({ policy, options?, writable?, approval?, approved? })` is the `authorize` option of `supportSessions`; it decides each start with `decideSupportSession` as the admin, and an `approval-required` start passes only once `approved(input, pending)` returns `{ approvedBy }` for an approver other than the admin. `subscriberPolicy({ policy, principals, events, row?, sender? })` filters outbox, webhook or realtime subscribers to those who may read the event (exact type, then the longest `prefix.*`, then `*`; a type with no entry reaches no one). `principalSource({ claims, identities? })` is a `PrincipalSource` over the claims better-supabase mints, for `decideFor`, `subscribersFor` and delivery; only links the app created through a signed-in flow or a verified address go in `identities`. `authorizationHooks({ policy, claims, service?, principals?, events?, approvers?, support?, tickets? })` is the `authorization` option of `createServer`: it answers automation publish and run-as, delegated OAuth scopes, `approverOf`, identities, delivery audiences, support sessions and run tickets from the claims better-supabase mints now, never from the request.
|
|
406
499
|
|
|
407
500
|
`claims` is whatever Supabase verified: `data.claims` from `supabase.auth.getClaims()` on an `@supabase/ssr` server client, or `jwtClaims` from `@supabase/server` (`ctx.jwtClaims` in `withSupabase` or a `pipeline`, and `c.var.jwtClaims` after a framework bridge such as `toHono([withClaims(), withPermDock()])`). `@supabase/server/adapters/*` and `c.var.supabaseContext` are deprecated and removed on 1 December 2026; do not write new code against them ([Hono, H3, Elysia and NestJS](https://permdock.com/docs/adapters/supabase#hono-h3-elysia-and-nestjs)). Pass `null` for anonymous callers; never `getSession().access_token` (unverified). An API-key auth mode (`secret`, `publishable`) has `jwtClaims: null` and is the anonymous subject, not a user.
|
|
408
501
|
|
|
@@ -411,14 +504,15 @@ Share links reach RLS through `exchangeCapability`; see the `permdock-credential
|
|
|
411
504
|
Supabase specifics for generated RLS (the generic flags and parity checks are in the `permdock-data` skill):
|
|
412
505
|
|
|
413
506
|
- Point `--seeds-out` at the migrations directory (`--seeds-out supabase/migrations/`) so `rls generate` writes a new timestamped seeds migration only when the `role_permissions` rows change; never rewrite an applied seeds migration in place.
|
|
414
|
-
-
|
|
415
|
-
-
|
|
507
|
+
- Declarative Schemas 2.0 (pg-delta) is the default. Set `[experimental.pgdelta] enabled = true` in `supabase/config.toml` (Supabase CLI 2.119 or later), then run `permdock rls generate --target sql --split helpers,seeds,indexes,policies,hook --seeds-out supabase/migrations/` with no `--out`: it writes `supabase/schemas/permdock/{helpers,indexes}.sql`, `public/policies/permdock.sql` and `permdock/functions/custom_access_token_hook.sql`, grants included. `supabase db schema declarative sync --no-apply -f <name>` writes the migration; the seeds migration must sort after it, so on the first run print the seeds (`--seeds-out -`), sync, then generate again. Never put the `role_permissions` rows in a schema file: pg-delta rejects data. Never pass `--grants-out` here; it is refused. `permdock doctor` PD068 fails while `config.toml` lacks `enabled = true`, and PD069 lists `schema_paths` and numbered parts left over from migra.
|
|
508
|
+
- Opt out only for a project that is not on pg-delta: `supabase.schemas: 'migra'` (or `[experimental.pgdelta] enabled = false`) for `supabase db diff`, `supabase.schemas: 'migrations'` for hand-written migrations. `--schemas` overrides both for one run ([schema workflow](https://permdock.com/docs/cli/rls#schema-workflow)).
|
|
509
|
+
- Under `migra`, generate `--split helpers,seeds,policies,hook --out supabase/schemas/identity/056_permdock_{part}.sql --seeds-out supabase/migrations/` and write the schema and function privileges and the `security_invoker` view option with `--grants-out` into a migration created by `supabase migration new` after the first `db diff`; `db diff` drops those, so never append them to a migration by hand. Table grants and policies stay in the schema files, where `db diff` keeps them. Keep the helpers part ahead of every file that calls the helpers in `schema_paths` (`permdock doctor` PD042, PD043).
|
|
416
510
|
- `--authorize jwt` reads the hook's claims: enable the printed `[auth.hook.custom_access_token]` stanza in `supabase/config.toml`. The generated hook writes the canonical `memberships` claim from the `rls.memberships.scopes` tables, so do not hand-write one.
|
|
417
|
-
- For more than one membership table, declare `
|
|
511
|
+
- For more than one membership table, declare `tableMembershipSource` / `junctionMembershipSource` sources once (from `permdock/supabase`, with a `query` on the server), pass the array as `memberships` (wrapped in `claimsFirst(sources, { version: authzVersion({ query }) })` to trust the token until it is truncated or stale), put the same sources under `supabase.hook.memberships` in `permdock.config.ts`, and run `permdock supabase hook generate`; add the printed `config.toml` block (`jwt_expiry = 900`). List member-removal and payout permissions in `definePolicy({ fresh })`.
|
|
418
512
|
- When the server has only supabase-js (no `SqlQuery`), never re-read memberships, global roles or custom roles with the admin client: grant `subject_for(p_user)` and `authz_version_for(p_user)` (written next to the hook) to the backend role, expose them through `public` wrappers when `permdock` is not an exposed schema, and use `postgrestSources(admin, { schema, fn, versionFn, policy })`: `claimsFirst(sources.memberships)` (with `onStale: 'reread'` a fresh token costs one version call, and with `policy` a token that claims a custom role costs one `subject_for` call), `customRoles: sources.customRoles(principal)`, and `await sources.subject(userId)` for a job acting for a stored user.
|
|
419
|
-
- When a role table stores a role id (`user_roles.role_id`, `organization_users.role_id` referencing `roles`), read the key through it with `{ through: 'roles', on: { role_id: 'id' }, column: 'key' }`: in `rls.roles`, in `
|
|
420
|
-
- When one membership row holds roles in several columns (a `tier` and a `role_id`), list them instead of building a union view: `role: ['tier', { through: 'roles', on: { role_id: 'id' }, column: 'key' }]` in `rls.memberships` and `
|
|
421
|
-
- When a membership table names a profile instead of a login (`customer_contacts.contact_profile_id` referencing `contact_profiles`, whose `user_id` is the login), read the user through it with `
|
|
513
|
+
- When a role table stores a role id (`user_roles.role_id`, `organization_users.role_id` referencing `roles`), read the key through it with `{ through: 'roles', on: { role_id: 'id' }, column: 'key' }`: in `rls.roles`, in `tableMembershipSource` `columns.role` or `junctionMembershipSource` `roles`, and in an `rls.memberships` table's `role`. Never copy keys into the membership table. Keep tenant custom-role keys apart from declared role names.
|
|
514
|
+
- When one membership row holds roles in several columns (a `tier` and a `role_id`), list them instead of building a union view: `role: ['tier', { through: 'roles', on: { role_id: 'id' }, column: 'key' }]` in `rls.memberships` and `tableMembershipSource` `columns.role`, `roles: { sources: [...] }` in `junctionMembershipSource`. The row holds every non-null key, and the holder-count triggers stay on the real table.
|
|
515
|
+
- When a membership table names a profile instead of a login (`customer_contacts.contact_profile_id` referencing `contact_profiles`, whose `user_id` is the login), read the user through it with `junctionMembershipSource` `user` or `tableMembershipSource` `columns.user`: `{ through: 'contact_profiles', on: { contact_profile_id: 'id' }, column: 'user_id' }`. Never copy `user_id` onto the membership rows. Keep `contact_profiles.user_id` server-owned (PD028).
|
|
422
516
|
- For attribute conditions (`principal.claims.attrs.region`), list server-owned columns or `app_metadata.<key>` in `supabase.hook.attrs`, never `user_metadata`, and keep clients from updating those columns (`permdock doctor` PD028).
|
|
423
517
|
|
|
424
518
|
## Supabase middleware pipeline — `permdock/supabase/middleware`
|
|
@@ -432,7 +526,9 @@ import { createPermDock } from "permdock/supabase/middleware";
|
|
|
432
526
|
const { withPermDock, permdockHandler } = createPermDock(policy, {
|
|
433
527
|
subject: (ctx) =>
|
|
434
528
|
subjectFromSupabase(ctx.jwtClaims, {
|
|
435
|
-
|
|
529
|
+
claims: {
|
|
530
|
+
roles: "user_role",
|
|
531
|
+
},
|
|
436
532
|
declared: ["member", "admin"],
|
|
437
533
|
}),
|
|
438
534
|
});
|
|
@@ -467,7 +563,7 @@ pipeline([withClaims()], permdockHandler());
|
|
|
467
563
|
## Remote PDP — `permdock/pdp`
|
|
468
564
|
|
|
469
565
|
```ts
|
|
470
|
-
import {
|
|
566
|
+
import { getPermDock, remotePdp } from "permdock/pdp";
|
|
471
567
|
|
|
472
568
|
const policy = definePolicy(permissions, {
|
|
473
569
|
roles: [member],
|
|
@@ -477,16 +573,16 @@ const policy = definePolicy(permissions, {
|
|
|
477
573
|
],
|
|
478
574
|
});
|
|
479
575
|
|
|
480
|
-
const permdock = await
|
|
576
|
+
const permdock = await getPermDock(policy, user);
|
|
481
577
|
await permdock.can(permissions.post.read, post);
|
|
482
578
|
```
|
|
483
579
|
|
|
484
|
-
Use `
|
|
580
|
+
Use `getPermDock` from `permdock/pdp` when `providers` is set. Core `can` / `decide` stay synchronous and deny delegated permissions with `pdp-unavailable`; on the PDP instance `filter` and `where` are async too.
|
|
485
581
|
|
|
486
582
|
For OpenFGA or SpiceDB, use `openfga({ url, storeId, map })` or `spicedb({ url, token, map })` as the provider. `map` is one `[permission, (subject, row) => tuple]` pair per delegated permission; `filter` and `where` then use `list-objects` / `LookupResources` ids, so the tuple ids must equal the resource's `id` field. The app writes the tuples.
|
|
487
583
|
|
|
488
584
|
## Collect-only frameworks (no package)
|
|
489
585
|
|
|
490
|
-
Nuxt,
|
|
586
|
+
Nuxt, React Router and Effect have no `permdock/<name>` entry. Add `createPermDockUnplugin.vite()` from `permdock/unplugin` (or `.webpack` / `.esbuild`). Runtime is `permdock/h3` for Nuxt server routes, otherwise `permdock/server` or the matching HTTP adapter, plus `permdock/vue`, `permdock/react` or `permdock/svelte` on the client. Effect Schema is a Standard Schema; Effect HttpApi uses Overlay, not a hook.
|
|
491
587
|
|
|
492
588
|
Remaining work follows the names on the adapter page under `/docs/adapters/<name>`. Do not invent identifiers.
|
|
@@ -4,7 +4,7 @@ Owning pages: [policies](https://permdock.com/docs/concepts/policies), [decision
|
|
|
4
4
|
|
|
5
5
|
## Usage limits
|
|
6
6
|
|
|
7
|
-
A `limit: { count, per }` grant needs `limits: memoryLimitStore()` (or the app's `LimitStore`) on `createPermDock`; without it the grant denies with `limit-unavailable`. `can` never consumes a unit: a mutation that spends one calls `decide` or `assert`.
|
|
7
|
+
A `limit: { count, per }` grant needs `limits: limits(memoryLimitStore())` (or `limits(store)` over the app's `LimitStore`) on `createPermDock`; without it the grant denies with `limit-unavailable`. `can` never consumes a unit: a mutation that spends one calls `decide` or `assert`.
|
|
8
8
|
|
|
9
9
|
- `mode: 'soft'` keeps granting past the count with an `over-limit` obligation; `alertAt: 0.8` adds a `near-limit` warning.
|
|
10
10
|
- A granted decision carries `quota: { remaining, resetsAt }`. An exhausted hard limit answers `429` with `Retry-After` and `RateLimit` headers; for the header format, defer to the `ratelimit-headers` spec skill (`npx skills add ScaleDockHQ/scaledock-skills --skill ratelimit-headers`).
|
|
@@ -22,7 +22,7 @@ Localise `describe` with `messages`. `messages.reasons` is a record per denial r
|
|
|
22
22
|
|
|
23
23
|
## Validity windows
|
|
24
24
|
|
|
25
|
-
Access that ends on a date (a contractor's engagement, a change freeze until launch) is `validFrom` / `validUntil` on the grant, RFC 3339 or
|
|
25
|
+
Access that ends on a date (a contractor's engagement, a change freeze until launch) is `validFrom` / `validUntil` on the grant, an RFC 3339 string or an `Instant` such as a `Temporal.Instant`. Outside the window an allow denies with `inactive-grant` and a deny does not apply; `where()` and generated RLS drop it, and `simulate(checks, { now })` previews an instant (`now` is an `Instant`). A person's access ending is the membership's `expiresAt` (see the `permdock-tenancy` skill).
|
|
26
26
|
|
|
27
27
|
## Step-up
|
|
28
28
|
|
|
@@ -33,13 +33,22 @@ Hardware-key step-up is `to: assurance({ amr: ['hwk'], maxAge: 300 })` and rende
|
|
|
33
33
|
Adopting PermDock in an app with its own keys, SQL helpers and tokens is a step-by-step guide ([existing apps](https://permdock.com/docs/getting-started/existing-apps)):
|
|
34
34
|
|
|
35
35
|
- Keep the app's keys. Map verbs such as `view` to SQL with `rls.actions`, and give colliding resources a distinct `name` on `resource()`.
|
|
36
|
-
- Put application-owned metadata in `meta.x`; PermDock carries it and never reads it.
|
|
36
|
+
- Put application-owned metadata in `meta.x`; PermDock carries it and never reads it (see App data below).
|
|
37
37
|
- Rename a key later with `definePermissions(tree, { renamed: { "old.key": "new.key" } })`. Remove the alias only when `permdock doctor` PD055 and PD056 are quiet; `permdock diff` reports the removal as the breaking `alias-removed`.
|
|
38
|
-
- Generate helpers beside legacy SQL with `permdock rls generate --helpers-only --shims`, then rewrite policies with `permdock rls migrate
|
|
38
|
+
- Generate helpers beside legacy SQL with `permdock rls generate --helpers-only --shims`, then rewrite policies with `permdock rls migrate`.
|
|
39
|
+
|
|
40
|
+
## App data and app obligations
|
|
41
|
+
|
|
42
|
+
Owning page: [extend PermDock](https://permdock.com/docs/guides/extending).
|
|
43
|
+
|
|
44
|
+
- Type `x` with one Standard Schema per kind: `definePermissions(tree, { x: { permission, resource } })`, `defineRoles(tree, { x })`, `definePlans(tree, { x })`, `definePolicy(…, { x: { grant, membership } })`. Each schema validates synchronously. Invalid `x` in code throws; invalid `Membership.x` or custom-role `meta.x` from a source is dropped and the row stays.
|
|
45
|
+
- Describe a grant with `allow(p, { name, meta: { description, x } })`. It reaches `decision.matched`, decision events, snapshot grants and catalog grants, and stays out of the policy fingerprint.
|
|
46
|
+
- Steer code on a grant with `allow(p, { obligations: ['watermark', { name, detail }] })`, never on a deny. Read `{ kind: 'app', name, detail? }` from `decision.obligations` on a granted decision.
|
|
47
|
+
- Snapshots carry grant `meta`, membership `x` and every leaf's `meta` to the client: never put a secret in them.
|
|
39
48
|
|
|
40
49
|
## CLI starting points
|
|
41
50
|
|
|
42
|
-
- When the app already has an OpenAPI document and no definitions yet, start from `permdock openapi import --doc <doc> --out src/permissions.generated.ts --
|
|
51
|
+
- When the app already has an OpenAPI document and no definitions yet, start from `permdock openapi import --doc <doc> --out src/permissions.generated.ts --validator zod` and review each action's `meta.inferredFrom`.
|
|
43
52
|
- When the repo has Arazzo workflows, run `permdock arazzo check --doc <arazzo> --openapi <doc>`.
|
|
44
|
-
- When the app uses PermDock Cloud, add `permdock cloud push` to the deploy step after the deploy, with `PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` from CI secrets. The Cloud is optional and never on the decision path.
|
|
53
|
+
- When the app uses PermDock Cloud, add `permdock cloud push` to the deploy step after the deploy, with `PERMDOCK_CLOUD_URL` and `PERMDOCK_CLOUD_KEY` from CI secrets; pass `--manifest` with the `supabase inspect --out` file when the repo has one. A repo that builds from a Cloud project runs `permdock cloud pull --project <id>` with `PERMDOCK_CLOUD_TOKEN` before its checks instead. Locally, `permdock login` replaces both keys. The Cloud is optional and never on the decision path.
|
|
45
54
|
- `permdock skills install` copies these skills into the project's agent folders, writing a symlinked folder once through its target; `permdock skills install --check` in CI fails when the installed skills differ from the package version.
|