{"_id":"@api-policy/server","_rev":"8-f604b62d96e051fafd57c8c721a299b8","name":"@api-policy/server","dist-tags":{"latest":"3.1.2"},"versions":{"1.0.0":{"name":"@api-policy/server","version":"1.0.0","keywords":["policy","authorization","auth","jwt","hmac","api-key","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@1.0.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"87c4ca2108b252e0b5fa82aaf238f4ffdf455d2c","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-1.0.0.tgz","fileCount":105,"integrity":"sha512-8xuWI15IrbQHFJiwFcybeuuLV6tMm8U6E8b52f4FUfR/fBSHk0byrJfKMzBiCLkLfUVCFzTzMUGp1M2NzuLsOg==","signatures":[{"sig":"MEYCIQDhYfHsG5IBEKFC0PD0dW7u/lnYE7c6Jc5Oqt4rCjAnKAIhAI5hncoZPkn0Z8IFdxTfOQdP0lU4w1TUEujcccRqRP1v","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":220714},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"c7f2fd5117e9eddda25427c587be46fa837a4dae","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Complete API authorization framework. JWT, HMAC, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"workspace:*"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.6","@types/jest":"^30.0.0"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_1.0.0_1773067706879_0.410492330525537","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@api-policy/server","version":"1.0.1","keywords":["policy","authorization","auth","jwt","hmac","api-key","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@1.0.1","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"c6eb9b13d86b282a5254aa1ec36d99b5282aea30","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-1.0.1.tgz","fileCount":105,"integrity":"sha512-9NWJOl6Xd2vC2kRQRsmjqd+Powh9cFHw+NXcptumLpFY/+mNThpSwNjTn63924BITI0hQz1gc2VBWxD2+4fRcQ==","signatures":[{"sig":"MEYCIQCE2KVBOdHWd11KMRBQybePMg0Ks3AFxrIRMPQD82SfkwIhAIGBhvnCFHVyVWP8LuHOQtitLGV3vvkre+7c6FFMmVfo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":220709},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"1dfb68d711990705804119c4cdce30bcd56a3656","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Complete API authorization framework. JWT, HMAC, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"^1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.6","@types/jest":"^30.0.0"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_1.0.1_1773102302863_0.14582445312801284","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@api-policy/server","version":"1.0.2","keywords":["policy","authorization","auth","jwt","hmac","api-key","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@1.0.2","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"19c219405f8263acdce5f890cce593e9f83cc3dd","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-1.0.2.tgz","fileCount":105,"integrity":"sha512-DsV6Djt0wQiDSDf2mUdPTYfYGyH+Rbano4iipAdAn5BimNzmpmJmhdv7k7Ak9yUnBUhTeKXOQa4KfYYbkdIcCQ==","signatures":[{"sig":"MEQCIDDKLDcgHyD0n7PvyGfGqCywbHstNVYJ7jfc4nh8CXC8AiBidIll5PkWMJATkgXwS0veeshSUdZOVoGJdTWIZwUKDg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":221094},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"1dfb68d711990705804119c4cdce30bcd56a3656","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Complete API authorization framework. JWT, HMAC, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.6","@types/jest":"^30.0.0"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_1.0.2_1773107060691_0.3015108220210956","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@api-policy/server","version":"1.0.3","keywords":["policy","authorization","auth","jwt","hmac","api-key","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@1.0.3","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"80e54f2f66d163aa58cb95f67a9120d771633ef4","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-1.0.3.tgz","fileCount":105,"integrity":"sha512-QQFEUHeuI87CqTXwctCX6AtSDswzzk0/UuO1JjbtK/eftDRLDvJy4bXXxbthODd0CeJOrSjNmnk3a9+DhhHV9Q==","signatures":[{"sig":"MEUCIEujEddwM8Wzk1QMTaejsKoWb3rOYX8Ddp73nxj0KfKAAiEAjuZ8zFTLSwm08BrU49LWNKLNSAVkhP6YC/P3t8JI8pQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":222745},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"f327e84a65c54d79fed8e22c01c2a5993fc74529","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Complete API authorization framework. JWT, HMAC, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.6","@types/jest":"^30.0.0"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_1.0.3_1773150106802_0.26503378332046545","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@api-policy/server","version":"2.0.0","keywords":["policy","authorization","auth","jwt","api-key","gateway-jwt","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@2.0.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"72e4d9599ca7257dbd658c91fbd9d956343c2071","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-2.0.0.tgz","fileCount":109,"integrity":"sha512-J/TbhAXrcqA+cxiQcmKq0vaO7MwjlzUrT7ZX3NSINn5rTHWj4otxC531YZKA3n0ILlwuapaKmYxsNe59HPL61w==","signatures":[{"sig":"MEQCIHArwJwHnIoDO9pe43W+zF49AU8dr8ffxbDP8uFOYSTLAiBlcGGofko6KX4ZuYtUU1mLZVb1BLQnLdsTfNpzLxTK+w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":239826},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"f327e84a65c54d79fed8e22c01c2a5993fc74529","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.6.2","description":"Complete API authorization framework. JWT, API Key, Gateway JWT, permissions, routing.","directories":{},"_nodeVersion":"24.11.1","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"^1.0.1"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","ts-jest":"^29.4.6","@types/jest":"^30.0.0"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_2.0.0_1773299791284_0.7303754356578791","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"@api-policy/server","version":"3.1.0","keywords":["policy","authorization","auth","jwt","api-key","gateway-jwt","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@3.1.0","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"b7831f342aac56f92d6b0d2867677e2aafee0fd9","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-3.1.0.tgz","fileCount":106,"integrity":"sha512-R+cXvm2GxGbb4WkGJUOeuq10E3Fhnu9tZrst7NNixBwDdH5BIX9HbH6tq+0VSbrXKyhOrqwwY88I0axmwvg0gg==","signatures":[{"sig":"MEQCIHlzjsPKgWj4o9XIIBQY5hX2Df9JzP+nsrgBoy9r6sDCAiBp+f2qif6aZv4Z2UlUYz0e6QKokmDfXalzQj9q3L/wEg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":201990},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"16ba5dc244324cebdcfc88b4c02266c9b22f4121","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.12.1","description":"Complete API authorization framework. JWT, API Key, Gateway JWT, permissions, routing.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"workspace:^"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^6.0.3","@types/node":"^25.9.3"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_3.1.0_1781191710029_0.4845550142154875","host":"s3://npm-registry-packages-npm-production"}},"3.1.1":{"name":"@api-policy/server","version":"3.1.1","keywords":["policy","authorization","auth","jwt","api-key","gateway-jwt","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","_id":"@api-policy/server@3.1.1","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"dist":{"shasum":"a638c05e661e711fd6ff1eaa42f4f267ced3505f","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-3.1.1.tgz","fileCount":106,"integrity":"sha512-nrrN7CD9RwXGPdHLGMUsx29FCuhb4E35s3oWUmbcg/cw32JOyD27pyU5hkgZ/oWMUD3dKow0J5xsi/CI2FR/7w==","signatures":[{"sig":"MEUCICDo/4rnDv/bM5FzZipg3wakjKwceenWYXE1D5envGSaAiEAy7jq8mxFo5OKYnHoPaExkyXho180jaY/1Kjjmk9vfr0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":201990},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"}},"gitHead":"16ba5dc244324cebdcfc88b4c02266c9b22f4121","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","type-check":"tsc --noEmit"},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"_npmVersion":"11.12.1","description":"Complete API authorization framework. JWT, API Key, Gateway JWT, permissions, routing.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"ts-micro-result":"^3.3.0","@api-policy/core":"workspace:^"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^6.0.3","@types/node":"^25.9.3"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/server_3.1.1_1781191741970_0.48102770285413166","host":"s3://npm-registry-packages-npm-production"}},"3.1.2":{"name":"@api-policy/server","version":"3.1.2","description":"Complete API authorization framework. JWT, API Key, Gateway JWT, permissions, routing.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./auth-methods":{"types":"./dist/auth-methods/index.d.ts","import":"./dist/auth-methods/index.js"},"./errors":{"types":"./dist/errors/index.d.ts","import":"./dist/errors/index.js"}},"scripts":{"build":"tsc","test":"vitest run","test:watch":"vitest","type-check":"tsc --noEmit","clean":"rm -rf dist"},"dependencies":{"@api-policy/core":"1.0.2","ts-micro-result":"^3.3.0"},"peerDependencies":{"jose":">=5.0.0"},"peerDependenciesMeta":{"jose":{"optional":true}},"keywords":["policy","authorization","auth","jwt","api-key","gateway-jwt","permissions","rbac"],"author":{"name":"minhtaimc"},"license":"MIT","devDependencies":{"@types/node":"^25.9.3","typescript":"^6.0.3","vitest":"^4.1.8"},"gitHead":"16ba5dc244324cebdcfc88b4c02266c9b22f4121","_id":"@api-policy/server@3.1.2","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-ILwRTfUChjGHubkapPkQN8+uVLpZW2rBW5UL/jxBo9cF/oscSuF97FAuvDy8UxT5C9OYHv77BJLomSFnep3cYg==","shasum":"4fb22fc98a6b3d60d1d23461003689260841ab16","tarball":"https://registry.npmjs.org/@api-policy/server/-/server-3.1.2.tgz","fileCount":106,"unpackedSize":201984,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICjxz09i4lO3fobmhi3iBguxswaKCqejI6WsNMiPDe/eAiAalxfRXjSIOtxyomDESIFIolWACSVAmPC/x3TH80jTsQ=="}]},"_npmUser":{"name":"minhtaimc","email":"minhtaimc@gmail.com"},"directories":{},"maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/server_3.1.2_1781193976460_0.5940168852474665"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-09T14:48:26.820Z","modified":"2026-06-11T16:06:16.710Z","1.0.0":"2026-03-09T14:48:27.038Z","1.0.1":"2026-03-10T00:25:03.006Z","1.0.2":"2026-03-10T01:44:20.863Z","1.0.3":"2026-03-10T13:41:46.961Z","2.0.0":"2026-03-12T07:16:31.586Z","3.1.0":"2026-06-11T15:28:30.215Z","3.1.1":"2026-06-11T15:29:02.105Z","3.1.2":"2026-06-11T16:06:16.599Z"},"author":{"name":"minhtaimc"},"license":"MIT","keywords":["policy","authorization","auth","jwt","api-key","gateway-jwt","permissions","rbac"],"description":"Complete API authorization framework. JWT, API Key, Gateway JWT, permissions, routing.","maintainers":[{"name":"minhtaimc","email":"minhtaimc@gmail.com"}],"readme":"# @api-policy/server\n\nAPI authorization framework for Node.js. JWT, API Key, and Gateway JWT authentication with a bitmask permission engine.\n\nBuilt on [@api-policy/core](../core). All core builders (`role`, `perm`, `owner`, `and`, `or`, `not`, `evaluate`, `explain`) are re-exported from this package — no need to install core separately.\n\n```bash\nnpm install @api-policy/server jose\n```\n\n---\n\n## Permission Model\n\nUsers carry per-resource bitmasks. Each resource is independent — `product` permissions never affect `order`.\n\n```ts\nimport { PERM, PERM_ALL } from '@api-policy/server'\n\n// Regular user\nuser.perms = {\n  product: PERM.READ | PERM.WRITE,  // = 3\n  order: PERM.READ,                  // = 1\n}\n\n// Admin: wildcard grants access to all resources\nuser.perms = { '*': PERM_ALL }  // = 31\n```\n\n**Permission bits:**\n\n| Constant | Value | Bit |\n|----------|-------|-----|\n| `PERM.READ` | 1 | `1 << 0` |\n| `PERM.WRITE` | 2 | `1 << 1` |\n| `PERM.DELETE` | 4 | `1 << 2` |\n| `PERM.APPROVE` | 8 | `1 << 3` |\n| `PERM.EXECUTE` | 16 | `1 << 4` |\n| `PERM_ALL` | 31 | all bits |\n\n**Resolution order:** resource-specific → wildcard `'*'` → 0 (deny)\n\nIf a user has both `product: 1` and `'*': 31`, the specific mask (`1`) wins for `product`.\n\n---\n\n## UserContext\n\nShape of the authenticated user passed through your system:\n\n```ts\ntype UserContext = {\n  id: string\n  perms?: Record<string, number>   // per-resource bitmasks\n  roles?: string[]                 // optional role array\n  tenantId?: string\n  tenantType?: string\n}\n```\n\nJWT payload expected by the default JWT auth method:\n\n```json\n{\n  \"sub\": \"user-123\",\n  \"perms\": { \"product\": 7, \"order\": 1 },\n  \"roles\": [\"editor\"],\n  \"tid\": \"tenant-abc\"\n}\n```\n\n---\n\n## Authentication\n\n### JWT\n\n```ts\nimport {\n  createAuthPolicyEngine,\n  createJwtAuthMethod,\n  createDefaultJwtVerify,\n} from '@api-policy/server'\n\n// Symmetric (HS256)\nconst jwtMethod = createJwtAuthMethod(\n  { secret: process.env.JWT_SECRET! },\n  createDefaultJwtVerify({ secret: process.env.JWT_SECRET! })\n)\n\n// Asymmetric (RS256, ES256, EdDSA, etc.)\nconst jwtMethod = createJwtAuthMethod(\n  { publicKey: process.env.JWT_PUBLIC_KEY!, algorithm: 'EdDSA' },\n  createDefaultJwtVerify({ publicKey: process.env.JWT_PUBLIC_KEY!, algorithm: 'EdDSA' })\n)\n\n// Custom verify function (e.g. JWKS endpoint)\nconst jwtMethod = createJwtAuthMethod(\n  { issuer: 'https://auth.example.com' },\n  async (token, config) => {\n    // your custom verification logic\n    // return ok(payload) or err({ code: 'TOKEN_EXPIRED', message: '...' })\n  }\n)\n```\n\nReads from `Authorization: Bearer <token>` header.\n\n### API Key\n\n```ts\nimport { createApiKeyAuthMethod } from '@api-policy/server'\n\nconst apiKeyMethod = createApiKeyAuthMethod({\n  lookup: async (key) => {\n    const record = await db.apiKeys.findOne({ key })\n    if (!record) return null\n    return {\n      ownerId: record.userId,\n      perms: { '*': PERM_ALL },\n      tenantId: record.tenantId,\n    }\n  },\n})\n```\n\nReads from `X-API-Key` header.\n\n### Gateway JWT\n\nFor BFF → backend flows. Verifies two JWTs per request: a short-lived Gateway JWT that identifies the trusted BFF, and the user's Access JWT forwarded from the client.\n\n```\nClient → BFF (verify access JWT, sign Gateway JWT) → Backend (verify both)\n```\n\n```ts\nimport { createGatewayJwtAuthMethod, createDefaultJwtVerify } from '@api-policy/server'\n\nconst gatewayMethod = createGatewayJwtAuthMethod({\n  gateway: {\n    // Ed25519 public key of the BFF (64 hex chars = 32 bytes)\n    // Omit for local dev — skips gateway verification entirely\n    publicKeyHex: process.env.BFF_PUBLIC_KEY_HEX,\n    expectedIssuer: 'bff-worker',\n    expectedAudience: 'my-backend',\n    // header: 'x-gateway-auth',  // default\n    // clockSkewSeconds: 5,       // default — keep ≤10s for NTP-synced envs\n    // validateJti: async (jti) => cache.setNX(jti, 1),  // optional replay protection\n  },\n  accessJwt: {\n    config: { secret: process.env.JWT_SECRET! },\n    verifyFn: createDefaultJwtVerify({ secret: process.env.JWT_SECRET! }),\n  },\n})\n```\n\n**Headers expected:**\n\n| Header | Value |\n|--------|-------|\n| `x-gateway-auth` | `Bearer <gateway_jwt>` — identifies the BFF |\n| `Authorization` | `Bearer <access_jwt>` — user's token (forwarded as-is) |\n\n**Gateway JWT payload** (identity only, no user context):\n\n```json\n{\n  \"iss\": \"bff-worker\",\n  \"aud\": \"my-backend\",\n  \"sub\": \"worker-id-1\",\n  \"exp\": \"<now + 30s>\",\n  \"jti\": \"<uuid>\"\n}\n```\n\nUser context is extracted from the **access JWT payload** (`sub`, `perms`, `tid`, `roles`) — never from headers.\n\n**Local dev mode** — omit `publicKeyHex` to skip gateway verification:\n\n```ts\ncreateGatewayJwtAuthMethod({\n  gateway: {\n    // no publicKeyHex → x-gateway-auth header not required\n    expectedIssuer: 'bff-worker',\n    expectedAudience: 'my-backend',\n  },\n  accessJwt: { config: { secret: 'dev-secret' }, verifyFn: createDefaultJwtVerify(...) },\n})\n```\n\n---\n\n## AuthPolicyEngine\n\nCombines multiple auth methods. Tries each in order until one succeeds.\n\n```ts\nimport { createAuthPolicyEngine, AUTH_METHOD } from '@api-policy/server'\n\nconst engine = createAuthPolicyEngine(\n  [jwtMethod, apiKeyMethod, gatewayMethod],\n  { defaultMethods: [AUTH_METHOD.JWT, AUTH_METHOD.API_KEY] }\n)\n\nconst result = await engine.resolve('jwt', ctx)\n\nif (result.ok) {\n  const { user, method } = result.data\n} else {\n  // result.errors[0] is a typed error (NotAuthenticated, InvalidCredentials, etc.)\n}\n```\n\n`AuthRequirement` can be:\n- `true` — try default methods\n- `'jwt'` | `'apiKey'` | `'gatewayJwt'` — specific method\n- `['jwt', 'apiKey']` — try list in order\n\n---\n\n## PermissionEngine\n\nStandalone permission checker. Used by adapters internally, or call directly in handlers.\n\n```ts\nimport {\n  createPermissionEngine,\n  PERM,\n  BOUNDARY,\n} from '@api-policy/server'\n\nconst engine = createPermissionEngine()\n\n// check() — full flow: roles → allowOwner → bitmask → boundary\nconst result = engine.check(\n  user,\n  {\n    resource: 'product',\n    action: PERM.WRITE,\n    boundary: BOUNDARY.TENANT,\n  },\n  { tenantId: 'tenant-abc', ownerId: 'user-123' }\n)\n\nif (!result.ok) {\n  throw result.error  // PermissionDenied or BoundaryViolation\n}\n\n// can() — simple bitmask check, no boundary\nif (engine.can(user, 'product', PERM.WRITE)) {\n  // user has WRITE on product\n}\n\n// compile() — pre-bind spec for repeated checks\nconst canDelete = engine.compile({\n  resource: 'product',\n  action: PERM.DELETE,\n  boundary: BOUNDARY.TENANT,\n})\n\nconst allowed = canDelete(user, resource)\n```\n\n### Permission check flow\n\n```\n1. Roles gate       — deny if user does not have required role(s)\n2. Owner bypass     — allow immediately if resource.ownerId === user.id (when allowOwner: true)\n3. Capability check — deny if (user.perms[resource] & action) !== action\n4. Boundary check   — deny if tenant/owner constraint not satisfied\n```\n\n### Boundaries\n\n| Value | Check |\n|-------|-------|\n| `BOUNDARY.GLOBAL` | No boundary check |\n| `BOUNDARY.TENANT` | `user.tenantId === resource.tenantId` |\n| `BOUNDARY.OWNER` | `user.id === resource.ownerId` |\n| `BOUNDARY.SELF` | Same as OWNER (alias) |\n\n### Roles gate\n\n```ts\n// OR logic (default): user has ANY of the listed roles\n{ roles: ['admin', 'moderator'], resource: 'post', action: PERM.DELETE, boundary: BOUNDARY.GLOBAL }\n\n// AND logic: user must have ALL listed roles\n{ roles: ['admin', 'superuser'], requireAllRoles: true, ... }\n```\n\nRoles are checked first. If the user fails the role check, `allowOwner` does not save it.\n\n### allowOwner\n\n```ts\nengine.check(user, {\n  resource: 'post',\n  action: PERM.WRITE,\n  boundary: BOUNDARY.GLOBAL,\n  allowOwner: true,\n}, { ownerId: post.authorId })\n```\n\nThe resource owner bypasses bitmask and boundary checks entirely. Non-owners go through the normal flow.\n\nDo **not** use `allowOwner` when owners should have restricted actions (e.g. cannot approve their own request).\n\n---\n\n## Route Definition\n\n```ts\nimport { defineRoute, definePublicRoute, PERM, BOUNDARY } from '@api-policy/server'\n\nconst getPost = defineRoute({\n  method: 'GET',\n  path: '/posts/:id',\n  auth: true,\n  permission: {\n    resource: 'post',\n    action: PERM.READ,\n    boundary: BOUNDARY.TENANT,\n  },\n  handler: async (ctx) => {\n    const post = await db.posts.findById(ctx.params.id)\n    return ok(post)\n  },\n})\n\nconst createPost = defineRoute({\n  method: 'POST',\n  path: '/posts',\n  auth: 'jwt',\n  permission: {\n    resource: 'post',\n    action: PERM.WRITE,\n    boundary: BOUNDARY.GLOBAL,\n    roles: ['editor', 'admin'],\n  },\n  input: { body: PostCreateSchema },\n  handler: async (ctx) => {\n    const post = await db.posts.create(ctx.body)\n    return ok(post)\n  },\n})\n\nconst publicHealth = definePublicRoute({\n  method: 'GET',\n  path: '/health',\n  handler: async () => ok({ status: 'ok' }),\n})\n```\n\n**Route config options:**\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `auth` | `boolean \\| AuthMethodName \\| AuthMethodName[]` | Auth requirement |\n| `permission` | `PermissionSpec` | Permission check |\n| `loadResource` | `(ctx) => Promise<ResourceContext>` | Load resource for boundary check |\n| `input` | `{ body?, query?, params? }` | Validation schemas (Zod/TypeBox) |\n| `successStatus` | `number` | Override response status (default: POST=201, others=200) |\n\n---\n\n## Admin pattern\n\nNo special bypass flag. Admin is a user with full permission on the wildcard resource:\n\n```ts\n// Assign in your auth method / lookup function\nuser.perms = { '*': PERM_ALL }\n\n// Or specific resources with full access\nuser.perms = {\n  '*': PERM_ALL,\n  'audit-log': PERM.READ,  // even admins can't write audit logs\n}\n```\n\nThe engine resolves: `perms['order'] ?? perms['*'] ?? 0`. Resource-specific mask always wins.\n\n---\n\n## Type-safe resources\n\nPass your resource union as a generic to catch typos at compile time:\n\n```ts\ntype Resource = 'product' | 'order' | 'post'\n\nconst user: UserContext<Resource> = {\n  id: 'user-123',\n  perms: {\n    product: PERM.READ | PERM.WRITE,\n    order: PERM.READ,\n    // post: ...  ← TS error if you typo 'psot'\n  },\n}\n\nconst spec: PermissionSpec<Resource> = {\n  resource: 'product',  // TS enforces valid resource names\n  action: PERM.WRITE,\n  boundary: BOUNDARY.GLOBAL,\n}\n```\n\n---\n\n## Using core builders in handlers\n\nAll `@api-policy/core` exports are available from `@api-policy/server`. Use `toSubject()` to bridge `UserContext` to the core engine's `subject` shape:\n\n```ts\nimport { evaluate, or, role, owner, toSubject } from '@api-policy/server'\n\nconst allowed = evaluate(\n  or(role('admin'), owner('authorId')),\n  {\n    subject: toSubject(ctx.user, 'post'),  // picks user.perms['post']\n    resource: post,\n  }\n)\n```\n\n---\n\n## Errors\n\nAll errors are typed and carry a `code` field:\n\n```ts\nimport {\n  NotAuthenticated,\n  InvalidCredentials,\n  TokenExpired,\n  TokenMalformed,\n  PermissionDenied,\n  BoundaryViolation,\n  Forbidden,\n  NotFound,\n  Conflict,\n  RateLimitExceeded,\n  InternalError,\n  getHttpStatus,\n} from '@api-policy/server'\n\n// Each error factory accepts an optional message:\nthrow PermissionDenied({ message: 'Insufficient role' })\n\n// Map to HTTP status:\ngetHttpStatus(error)  // → 403\n```\n\n---\n\n## Build-time tools\n\n### Route linter\n\nCatch misconfigured routes at build time:\n\n```ts\nimport { lintRoutes, formatLintResult } from '@api-policy/server'\n\nconst result = lintRoutes(routes)\nif (!result.valid) {\n  console.error(formatLintResult(result))\n  process.exit(1)\n}\n```\n\n### Permission registry\n\nValidate that all `perms` references in your routes match your declared permissions:\n\n```ts\nimport { createPermissionRegistry, validatePermissions } from '@api-policy/server'\n\nconst registry = createPermissionRegistry({\n  product: { READ: true, WRITE: true, DELETE: true },\n  order: { READ: true },\n})\n\nconst result = validatePermissions(routes, registry)\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}