@venizia/ignis-docs 0.2.0 → 0.2.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,7 +1,19 @@
1
- # Authorization -- Error Reference
1
+ # Authorization - Error Reference
2
2
 
3
3
  > Complete error messages and troubleshooting for the authorization module. See [Setup & Configuration](./) for initial setup.
4
4
 
5
+ ## Find what you need
6
+
7
+ | You want to | Go to |
8
+ |---|---|
9
+ | See every error message, status code, and cause in one table | [Complete Error Reference](#complete-error-reference) |
10
+ | See which pipeline step throws which error | [Error Flow Diagram](#error-flow-diagram) |
11
+ | Fix a specific error message you're staring at | [Troubleshooting](#troubleshooting) |
12
+ | Debug "authorization does nothing" during rollout | [Authorization is not running](#authorization-is-not-running) |
13
+ | Debug rules rebuilding when you expected a cache hit | [Rules are rebuilt on every request](#rules-are-rebuilt-on-every-request) |
14
+ | Debug policies that don't seem to load | [Casbin policies not loading](#casbin-policies-not-loading) |
15
+ | Debug a Redis cache that never hits | [Redis cache not working](#redis-cache-not-working) |
16
+
5
17
  ## Error Flow Diagram
6
18
 
7
19
  ```mermaid
@@ -16,93 +28,102 @@ flowchart TD
16
28
  S4 -->|DENY| E403a[/"403: Authorization denied by voter"/]
17
29
  S4 -->|ALLOW/ABSTAIN| S5{"Step 5: Enforcers registered?"}
18
30
 
19
- S5 -->|No enforcers| OK3([No error - skip])
31
+ S5 -->|No enforcers, defaultDecision: allow| OK3([No error - allow, warning logged])
32
+ S5 -->|No enforcers, defaultDecision: deny or unset| E403c[/"403: no enforcer registered"/]
20
33
  S5 -->|Yes| S5b{"Resolve enforcer"}
21
- S5b -->|Name not found| E500b[/"500: Descriptor not found"/]
22
- S5b -->|DI fails| E500c[/"500: Failed to resolve"/]
34
+ S5b -->|Name not found| E400b[/"400: Descriptor not found"/]
35
+ S5b -->|DI fails| E400c[/"400: Failed to resolve"/]
23
36
  S5b -->|OK| S6{"Step 6: Build rules"}
24
37
 
25
- S6 -->|Not configured| E500d[/"500: Not configured. Call configure() first."/]
26
- S6 -->|No FilteredAdapter| E500e[/"500: Adapter does not support loadFilteredPolicy"/]
27
- S6 -->|Empty Redis key| E400b[/"400: keyFn returned an empty cache key"/]
38
+ S6 -->|Not configured| E400d[/"400: Not configured. Call configure() first."/]
39
+ S6 -->|No FilteredAdapter| E400e[/"400: Adapter does not support loadFilteredPolicy"/]
40
+ S6 -->|Empty Redis key| E400f[/"400: keyFn returned an empty cache key"/]
41
+ S6 -->|No principalType| E400g[/"400: principalType is required"/]
28
42
  S6 -->|OK| S7{"Step 7: Evaluate"}
29
43
 
30
- S7 -->|No action/resource| E500g[/"500: request.action and resource required"/]
44
+ S7 -->|No action/resource| E400h[/"400: action and resource required"/]
31
45
  S7 -->|ALLOW| OK4([Authorized])
32
- S7 -->|DENY| E403b[/"403: Authorization denied"/]
46
+ S7 -->|DENY / ABSTAIN-as-deny| E403b[/"403: Authorization denied"/]
47
+
48
+ style E401 fill:#f8d7da,stroke:#dc3545
49
+ style E403a fill:#f8d7da,stroke:#dc3545
50
+ style E403b fill:#f8d7da,stroke:#dc3545
51
+ style E403c fill:#f8d7da,stroke:#dc3545
52
+ style E400b fill:#fff3cd,stroke:#ffc107
53
+ style E400c fill:#fff3cd,stroke:#ffc107
54
+ style E400d fill:#fff3cd,stroke:#ffc107
55
+ style E400e fill:#fff3cd,stroke:#ffc107
56
+ style E400f fill:#fff3cd,stroke:#ffc107
57
+ style E400g fill:#fff3cd,stroke:#ffc107
58
+ style E400h fill:#fff3cd,stroke:#ffc107
33
59
  ```
34
60
 
35
61
  ## Complete Error Reference
36
62
 
37
- All error messages from the authorization module, organized by source:
63
+ All error messages from the authorization module, organized by source. **Status** is what `error.statusCode` holds. Most calls use `getError({ message })` with no explicit `statusCode`, so the shared `ApplicationError` defaults it to **400**. Only the request-pipeline errors below (steps 2, 4, 7) set an explicit `401`/`403`.
64
+
65
+ ### Component Errors (`AuthorizeComponent`)
66
+
67
+ Thrown during `binding()`, at application startup.
68
+
69
+ | Error Message | Status | Method | Cause |
70
+ |---------------|--------|--------|-------|
71
+ | `[AuthorizeComponent] No authorize options found. Bind options to AuthorizeBindingKeys.OPTIONS before registering the component.` | 400 | `binding` | `IAuthorizeOptions` was never bound before `this.component(AuthorizeComponent)` |
72
+
73
+ ### Enforcer Registry Errors (`AuthorizationEnforcerRegistry` + inherited `AbstractAuthRegistry`)
74
+
75
+ | Error Message | Status | Method | Cause |
76
+ |---------------|--------|--------|-------|
77
+ | <code v-pre>[getKey] Invalid name &#124; name: {{name}}</code> | 400 | `getKey` | Enforcer name is empty or falsy |
78
+ | `[AuthorizationEnforcerRegistry] No items registered` | 400 | `getDefaultName` | `getDefaultEnforcerName()` called with zero enforcers registered |
79
+ | <code v-pre>[AuthorizationEnforcerRegistry] Duplicate enforcer name(s): {{names}}</code> | 400 | `register` | Two or more enforcers in the same `register()` call share a name |
80
+ | <code v-pre>[AuthorizationEnforcerRegistry] Enforcer already registered: {{name}}</code> | 400 | `register` | An enforcer with this name was already registered in a previous call |
81
+ | <code v-pre>[AuthorizationEnforcerRegistry] Descriptor not found: {{name}}</code> | 400 | `resolveDescriptor` | `enforcerName` doesn't match any registered enforcer |
82
+ | <code v-pre>[AuthorizationEnforcerRegistry] Failed to resolve: {{name}}</code> | 400 | `resolveDescriptor` | The registered class has unsatisfied `@inject` dependencies |
83
+ | <code v-pre>[AuthorizationEnforcerRegistry] Enforcer "{{name}}" does not support cache invalidation</code> | 400 | `invalidateUserCache` / `rebuildUserCache` | The resolved enforcer doesn't implement the optional cache-management methods |
38
84
 
39
- ### Component Errors (AuthorizeComponent)
85
+ > [!NOTE]
86
+ > `[AuthorizationEnforcerRegistry] No items registered` can only surface if `getDefaultEnforcerName()` is called directly. During normal middleware execution, the provider checks `registry.hasEnforcers()` first and denies (or, with `defaultDecision: 'allow'`, proceeds) before ever calling `getDefaultEnforcerName()`, so it never reaches this throw.
40
87
 
41
- | Error Message | Status | Method |
42
- |---------------|--------|--------|
43
- | `[AuthorizeComponent] No authorize options found. Bind options to AuthorizeBindingKeys.OPTIONS before registering the component.` | 500 | `binding` |
88
+ ### Authorization Provider Errors (`AuthorizationProvider` - the request pipeline)
44
89
 
45
- ### Authorization Provider Errors
90
+ The only errors in the module with an **explicit** `statusCode`.
46
91
 
47
- | Error Message | Status | Step |
48
- |---------------|--------|------|
49
- | `Authorization failed: No authenticated user found` | 401 | Step 2 -- User check |
50
- | `Authorization failed: user.principalType is required for enforcer-based authorization` | 400 | Step 6 -- Build rules |
51
- | <code v-pre>Authorization denied by voter &#124; action: {{action}} &#124; resource: {{resource}}</code> | 403 | Step 4 -- Voter DENY |
52
- | <code v-pre>Authorization denied &#124; action: {{action}} &#124; resource: {{resource}}</code> | 403 | Step 7 -- Enforcer denied |
92
+ | Error Message | Status | Pipeline step | Cause |
93
+ |---------------|--------|------|-------|
94
+ | `Authorization failed: No authenticated user found` | 401 | 2 - User check | `Authentication.CURRENT_USER` is missing from the Hono context |
95
+ | <code v-pre>Authorization denied by voter &#124; action: {{action}} &#124; resource: {{resource}}</code> | 403 | 4 - Voters | A voter function returned `AuthorizationDecisions.DENY` |
96
+ | <code v-pre>Authorization failed: authorize() was declared for this route but no enforcer is registered &#124; path: {{path}}</code> | 403 | 5 - Resolve enforcer | `hasEnforcers()` is `false` and `defaultDecision` is `'deny'` (the default) or unset |
97
+ | `Authorization failed: user.principalType is required for enforcer-based authorization` | 400 | 6 - Build rules | The authenticated user object has no `principalType` field |
98
+ | <code v-pre>Authorization denied &#124; action: {{action}} &#124; resource: {{resource}}</code> | 403 | 7 - Evaluate | The enforcer returned `DENY`, or `ABSTAIN` and `defaultDecision` resolved to `'deny'` |
53
99
 
54
- ### Enforcer Registry Errors (AuthorizationEnforcerRegistry)
100
+ ### Casbin Enforcer Errors - Startup (`CasbinAuthorizationEnforcer.configure()`)
55
101
 
56
- | Error Message | Status | Method |
57
- |---------------|--------|--------|
58
- | <code v-pre>[getKey] Invalid name &#124; name: {{name}}</code> | 500 | `getKey` |
59
- | `[AuthorizationEnforcerRegistry] No items registered` | 500 | `getDefaultName` |
60
- | <code v-pre>[AuthorizationEnforcerRegistry] Duplicate enforcer name(s): {{names}}</code> | 500 | `register` |
61
- | <code v-pre>[AuthorizationEnforcerRegistry] Enforcer already registered: {{name}}</code> | 500 | `register` |
62
- | <code v-pre>[AuthorizationEnforcerRegistry] Descriptor not found: {{name}}</code> | 500 | `resolveDescriptor` |
63
- | <code v-pre>[AuthorizationEnforcerRegistry] Failed to resolve: {{name}}</code> | 500 | `resolveDescriptor` |
102
+ | Error Message | Status | Method | Cause |
103
+ |---------------|--------|--------|-------|
104
+ | `[CasbinAuthorizationEnforcer] "casbin" is not installed` | 400 | `configure` | The optional `casbin` peer dependency isn't installed |
105
+ | `[CasbinAuthorizationEnforcer] options.model is required.` | 400 | `configure` | `model` is missing from the enforcer options |
106
+ | <code v-pre>[registerMatchers] Role definition "{{name}}" is not declared in the Casbin model. Declare it under [role_definition] (e.g. `g = _, _, _`) before enabling domainMatching.</code> | 400 | `configure` (via `registerMatchers`) | `domainMatching.roleDefinition` isn't declared in `[role_definition]` (only checked when `domainMatching` is set) |
107
+ | <code v-pre>[CasbinAuthorizationEnforcer] Matcher smoke test failed at warmup - the model matcher did not compile ... {{error}}</code> | 400 | `configure` (via `assertMatcherCompilesSync`) | The matcher expression doesn't compile: syntax error, an unregistered function, or an arity mismatch |
108
+ | <code v-pre>[CasbinAuthorizationEnforcer] cached.options.expiresIn must be >= 10000 (ms) &#124; Received: {{value}}</code> | 400 | `configure` (via `validateExpiresIn`) | `cached.options.expiresIn` is below `MIN_EXPIRES_IN` (10,000 ms) |
109
+ | <code v-pre>[resolveDomainMatchingFn] Unsupported func: {{name}} &#124; Valids: [...]</code> | 400 | `configure` (via `registerMatchers`) | `domainMatching.fn` isn't a `CasbinDomainMatchingFunctions` value |
110
+ | <code v-pre>[resolveModel] Invalid model.driver &#124; Valids: [file, text]</code> | 400 | `configure` (via `resolveModel`) | `model.driver` isn't `'file'` or `'text'` |
64
111
 
65
112
  > [!NOTE]
66
- > The `[AuthorizationEnforcerRegistry] No items registered` error can only occur if `getDefaultEnforcerName()` is called directly. During normal middleware execution, the provider checks `registry.hasEnforcers()` first and skips authorization if no enforcers are registered, so this error is not thrown in the standard pipeline.
67
-
68
- ### Casbin Enforcer Errors (CasbinAuthorizationEnforcer)
69
-
70
- | Error Message | Status | Method |
71
- |---------------|--------|--------|
72
- | `[CasbinAuthorizationEnforcer] "casbin" is not installed` | 500 | `configure` |
73
- | `[CasbinAuthorizationEnforcer] options.model is required.` | 500 | `configure` |
74
- | `[CasbinAuthorizationEnforcer] Not configured. Call configure() first.` | 500 | `evaluate`, `loadPolicyLinesIntoModel` |
75
- | `[extractUserLines] Adapter does not support loadFilteredPolicy.` | 500 | `buildRules` (via `extractUserLines`) |
76
- | `[CasbinAuthorizationEnforcer] request.action and request.resource are required.` | 500 | `evaluate` |
77
- | `[CasbinAuthorizationEnforcer] keyFn returned an empty cache key.` | 400 | `buildRules`/cache management (via `resolveCacheKey`) |
78
- | <code v-pre>[CasbinAuthorizationEnforcer] cached.options.expiresIn must be >= 10000 (ms) &#124; Received: {{value}}</code> | 500 | `configure` (via `validateExpiresIn`) |
79
- | <code v-pre>[CasbinAuthorizationEnforcer] Matcher smoke test failed at warmup - ...</code> | 500 | `configure` (via `assertMatcherCompilesSync`) |
80
- | <code v-pre>[resolveDomainMatchingFn] Unsupported func: {{name}} &#124; Valids: [...]</code> | 500 | `configure` (via `registerMatchers`) |
81
- | <code v-pre>[registerMatchers] Role definition "{{name}}" is not declared in the Casbin model. ...</code> | 500 | `configure` (via `registerMatchers`, only when `domainMatching` is set) |
82
- | <code v-pre>[resolveModel] Invalid model.driver &#124; Valids: [file, text]</code> | 500 | `configure` (via `resolveModel`) |
83
- | `[CasbinAuthorizationEnforcer] Cache management requires the redis cache driver, but caching is disabled.` | 500 | `invalidateUserCache`/`rebuildUserCache` (via `requireRedisCache`) |
84
-
85
- > The `Invalid cached.driver` errors were removed - `cached` is now a typed union
86
- > (`{ use: false } | { use: true, driver: 'redis', ... }`), so an invalid driver is a compile-time
87
- > error, not a runtime one.
88
-
89
- ### Policy Loading Errors (CasbinAuthorizationEnforcer internals)
90
-
91
- | Error Message | Status | Method |
92
- |---------------|--------|--------|
93
- | `[CasbinAuthorizationEnforcer] keyFn returned an empty cache key.` | 400 | `resolveCacheKey` (read + cache-management paths) |
94
- | `[extractUserLines] Adapter does not support loadFilteredPolicy.` | 500 | `extractUserLines` |
95
- | `[loadPolicyLinesIntoModel] Not configured. Call configure() first.` | 500 | `loadPolicyLinesIntoModel` |
96
- | `[CasbinAuthorizationEnforcer] Cached payload is not an array of policy lines.` | - (logged, not thrown) | `parseCachedPolicyLines` - corrupt entry is discarded + refetched |
97
-
98
- > A corrupted Redis cache entry does **not** raise an error - it is logged and discarded, and the
99
- > lines are refetched from the adapter (the request never 500s on cache corruption).
100
-
101
- ### Registry Errors (AuthorizationEnforcerRegistry)
102
-
103
- | Error Message | Status | Method |
104
- |---------------|--------|--------|
105
- | `[AuthorizationEnforcerRegistry] Enforcer "{{name}}" does not support cache invalidation` | 500 | `invalidateUserCache` / `rebuildUserCache` (the resolved enforcer lacks the optional method) |
113
+ > `cached` is a typed discriminated union (`{ use: false } | { use: true; driver: 'redis'; ... }`), so an unsupported cache driver fails at compile time - there is no `Invalid cached.driver` runtime error. Caching is **Redis-only**.
114
+
115
+ ### Casbin Enforcer Errors - Runtime (`buildRules` / `evaluate` / cache management)
116
+
117
+ | Error Message | Status | Method | Cause |
118
+ |---------------|--------|--------|-------|
119
+ | `[CasbinAuthorizationEnforcer] Not configured. Call configure() first.` | 400 | `evaluate` | `evaluate()` ran before `configure()` built the enforcer pool - should not happen via `resolveEnforcer()` |
120
+ | `[CasbinAuthorizationEnforcer] request.action and request.resource are required.` | 400 | `evaluate` | Malformed `IAuthorizationRequest` passed to `evaluate()` |
121
+ | `[CasbinAuthorizationEnforcer] keyFn returned an empty cache key.` | 400 | `resolveCacheKey` (via `buildRules`, `invalidateUserCache`, `rebuildUserCache`) | `cached.options.keyFn` returned a falsy value for this user |
122
+ | `[CasbinAuthorizationEnforcer] Cache management requires the redis cache driver, but caching is disabled.` | 400 | `invalidateUserCache` / `rebuildUserCache` (via `requireRedisCache`) | Called with `cached: { use: false }` |
123
+ | `[extractUserLines] Adapter does not support loadFilteredPolicy.` | 400 | `buildRules` (via `extractUserLines`) | `options.adapter` doesn't implement casbin's `FilteredAdapter.loadFilteredPolicy` |
124
+ | `[loadPolicyLinesIntoModel] Not configured. Call configure() first.` | 400 | `evaluate` (via `loadPolicyLinesIntoModel`) | Same root cause as the `evaluate` "Not configured" row, different call site |
125
+
126
+ **Not an error** - `[CasbinAuthorizationEnforcer] Cached payload is not an array of policy lines.` `parseCachedPolicyLines` logs this at `warn` level; it never throws up to the caller. A corrupted or legacy Redis entry is discarded, and the lines are refetched from the adapter. The request never 500s (or 400s) on cache corruption.
106
127
 
107
128
  ## Troubleshooting
108
129
 
@@ -110,85 +131,85 @@ All error messages from the authorization module, organized by source:
110
131
 
111
132
  **Cause:** `AuthorizeComponent` was registered but `IAuthorizeOptions` was not bound to the container.
112
133
 
113
- **Fix:** Bind options **before** registering the component:
134
+ **Fix:** Bind options **before** registering the component.
114
135
 
115
136
  ```typescript
116
- // 1. Bind options first
117
137
  this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({
118
138
  defaultDecision: 'deny',
119
139
  alwaysAllowRoles: ['999_super-admin'],
120
140
  });
121
141
 
122
- // 2. Then register the component
123
142
  this.component(AuthorizeComponent);
124
143
  ```
125
144
 
126
145
  ### "Authorization failed: No authenticated user found"
127
146
 
128
- **Cause:** The authorization middleware runs after authentication, but no user was found on the context (`Authentication.CURRENT_USER` is undefined). This happens when:
129
- - The route has `authorize` but no `authenticate` config
130
- - Authentication middleware failed silently
131
- - Authentication was skipped but authorization was not
147
+ **Cause:** The authorization middleware runs after authentication, but no user was found on the context (`Authentication.CURRENT_USER` is `undefined`). Common triggers:
132
148
 
133
- **Fix:** Ensure routes with `authorize` also have `authenticate`:
149
+ - The route has `authorize` but no `authenticate`.
150
+ - Authentication was skipped, but authorization wasn't.
151
+ - The request genuinely has no valid credentials.
152
+
153
+ **Fix:** Every route with `authorize` needs a matching `authenticate` config.
134
154
 
135
155
  ```typescript
136
156
  this.defineRoute({
137
157
  configs: {
138
158
  path: '/',
139
159
  method: 'get',
140
- authenticate: { strategies: [Authentication.STRATEGY_JWT] }, // Must be present
160
+ authenticate: { strategies: [Authentication.STRATEGY_JWT] }, // must be present
141
161
  authorize: { action: AuthorizationActions.READ, resource: 'Article' },
142
162
  // ...
143
163
  },
144
- handler: async (context) => { ... },
164
+ handler: async context => { /* ... */ },
145
165
  });
146
166
  ```
147
167
 
148
168
  ### "Authorization failed: user.principalType is required"
149
169
 
150
- **Cause:** The authenticated user object does not have a `principalType` field. This is required for enforcer-based authorization because the enforcer uses `principalType` to construct the casbin subject (e.g., `user_123`, `service_456`).
170
+ **Cause:** The authenticated user object has no `principalType` field. Enforcer-based authorization uses it to build the casbin subject - for example, `User_123`.
151
171
 
152
- **Fix:** Ensure your authentication service sets `principalType` on the user object:
172
+ **Fix:** Set `principalType` when you build the user in your authentication service or token payload.
153
173
 
154
174
  ```typescript
155
- // In your JWT token service or authentication service:
156
175
  return {
157
176
  userId: '123',
158
- principalType: 'user', // Required for authorization
177
+ principalType: 'User', // required for authorization
159
178
  roles: [...],
160
179
  };
161
180
  ```
162
181
 
163
182
  > [!NOTE]
164
- > `principalType` is accessed via the `IAuthUser` index signature (`[extra: string | symbol]: any`), not a dedicated field. It must be set as a property on the returned user object.
183
+ > `principalType` is read via the `IAuthUser` index signature, not a dedicated field - it must be a real property on the returned user object.
165
184
 
166
185
  ### "Authorization denied by voter | action: ... | resource: ..."
167
186
 
168
- **Cause:** A voter function explicitly returned `AuthorizationDecisions.DENY` for the request.
187
+ **Cause:** A voter function explicitly returned `AuthorizationDecisions.DENY`.
188
+
189
+ **Fix:** Inspect the voter named in your logs. Common causes:
169
190
 
170
- **Fix:** Check the voter logic. Review which voter denied the request by examining the action and resource in the error message. Common causes:
171
- - Voter checking ownership and user is not the owner
172
- - Voter checking time window and request is outside allowed hours
173
- - Voter checking resource state (e.g., locked, archived)
191
+ - An ownership check failed.
192
+ - A time-window check rejected the request.
193
+ - A resource-state check (locked/archived) blocked it.
174
194
 
175
195
  ### "Authorization denied | action: ... | resource: ..."
176
196
 
177
- **Cause:** The enforcer's `evaluate()` returned `DENY` or `ABSTAIN` (which fell back to `defaultDecision`) for the requested action/resource. This means:
178
- - No matching policies were found for the user
179
- - Matching policies exist but deny the action
180
- - The casbin model does not cover the requested action/resource combination
197
+ **Cause:** `enforcer.evaluate()` returned `DENY`, or `ABSTAIN` and it fell back to `defaultDecision`. Usually one of:
198
+
199
+ - No matching policy for the user.
200
+ - A matching `deny` policy.
201
+ - The model doesn't cover the requested domain/resource/action combination.
181
202
 
182
- **Fix:** Debug by checking:
203
+ **Fix:** Work through this checklist:
204
+
205
+ 1. **Policies are loaded correctly** - verify the adapter returns the right rows for this user (`ScopedCasbinAdapter` reads `PolicyDefinition` filtered by `subject_type`/`subject_id`).
206
+ 2. **Subject format matches.** `normalizePayloadFn` (or the default scoped payload) must produce a subject that matches what's stored - for example, `User_123`.
207
+ 3. **The model covers the request.** For a custom (non-scoped) `.conf`, confirm the matcher handles the action/resource/domain shape you're sending.
208
+ 4. **Set `defaultDecision` explicitly** - don't rely on an implicit fallback:
183
209
 
184
- 1. **Policies are loaded correctly** -- verify your adapter is returning the right policy definitions for the user
185
- 2. **Subject format matches** -- the `normalizePayloadFn` must produce subjects matching your policy definitions (e.g., `user_123` must match what's in the database)
186
- 3. **Casbin model covers the request** -- your `.conf` file must define matchers for the action/resource/domain pattern you're using
187
- 4. **Set defaultDecision explicitly:**
188
210
  ```typescript
189
211
  this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({
190
- defaultDecision: 'deny', // Explicit is better
191
- // ...
212
+ defaultDecision: 'deny', // explicit is better than implicit
192
213
  });
193
214
  ```
194
215
 
@@ -196,108 +217,89 @@ this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({
196
217
 
197
218
  **Cause:** Two or more enforcers in the same `register()` call have the same name.
198
219
 
199
- **Fix:** Ensure each enforcer has a unique name:
220
+ **Fix:** Give every enforcer in the call a unique name.
200
221
 
201
222
  ```typescript
202
223
  AuthorizationEnforcerRegistry.getInstance().register({
203
224
  container: this,
204
225
  enforcers: [
205
- { enforcer: CasbinEnforcer, name: 'casbin', type: 'casbin', ... },
206
- { enforcer: CustomEnforcer, name: 'custom', type: 'custom', ... }, // Different name
226
+ { enforcer: CasbinAuthorizationEnforcer, name: 'casbin', type: 'casbin', options: { /* ... */ } },
227
+ { enforcer: MyCustomEnforcer, name: 'custom', type: 'custom' }, // different name
207
228
  ],
208
229
  });
209
230
  ```
210
231
 
211
232
  ### "[AuthorizationEnforcerRegistry] Enforcer already registered: ..."
212
233
 
213
- **Cause:** An enforcer with this name was already registered in a previous `register()` call. The registry does not allow overwriting.
214
-
215
- **Fix:** Ensure you only register each enforcer name once. If you need to re-register, call `registry.reset()` first (typically only in tests).
216
-
217
- ### "[AuthorizationEnforcerRegistry] No items registered"
218
-
219
- **Cause:** Tried to get the default enforcer name but no enforcers are registered. This error can only occur if `getDefaultEnforcerName()` is called directly. During normal middleware execution, the provider checks `registry.hasEnforcers()` first and skips authorization if no enforcers are registered, so this error is not triggered in the standard pipeline.
234
+ **Cause:** An enforcer with this name was already registered in a previous `register()` call - the registry doesn't allow overwriting.
220
235
 
221
- **Fix:** Register enforcers after registering the component:
222
-
223
- ```typescript
224
- // 1. Bind options and register component
225
- this.bind<IAuthorizeOptions>({ key: AuthorizeBindingKeys.OPTIONS }).toValue({ ... });
226
- this.component(AuthorizeComponent);
227
-
228
- // 2. Register enforcers
229
- AuthorizationEnforcerRegistry.getInstance().register({
230
- container: this,
231
- enforcers: [{ enforcer: CasbinAuthorizationEnforcer, name: 'casbin', type: 'casbin', options: { ... } }],
232
- });
233
- ```
236
+ **Fix:** Register each name once. Call `AuthorizationEnforcerRegistry.getInstance().reset()` first if you genuinely need to re-register (typically only in tests).
234
237
 
235
238
  ### "[AuthorizationEnforcerRegistry] Descriptor not found: ..."
236
239
 
237
- **Cause:** Tried to resolve an enforcer by a name that was never registered. This happens when `enforcerName` is specified in the `authorize()` call but doesn't match any registered enforcer.
240
+ **Cause:** `enforcerName` in an `authorize()` call doesn't match any registered enforcer name.
238
241
 
239
- **Fix:** Ensure the enforcer name matches what was registered. The default enforcer name is the first one registered.
242
+ **Fix:** Match the name used in `register()`. The default (when `enforcerName` is omitted) is the first one registered.
240
243
 
241
244
  ### "[AuthorizationEnforcerRegistry] Failed to resolve: ..."
242
245
 
243
- **Cause:** The enforcer class was registered in the descriptor Map but DI container resolution returned `null`. This typically means the enforcer class has unsatisfied dependencies.
246
+ **Cause:** The enforcer class is registered, but DI resolution returned `null` - typically an unsatisfied `@inject` dependency in its constructor.
244
247
 
245
- **Fix:** Check that all `@inject` dependencies in your enforcer's constructor are bound in the same container.
248
+ **Fix:** Confirm every `@inject` in the enforcer's constructor is bound in the same container before `resolveEnforcer()` runs.
246
249
 
247
- ### "[CasbinAuthorizationEnforcer] casbin is not installed"
250
+ ### "[CasbinAuthorizationEnforcer] "casbin" is not installed"
248
251
 
249
- **Cause:** The Casbin enforcer dynamically imports `casbin` at configure time, but the package is not installed.
252
+ **Cause:** The Casbin enforcer dynamically imports `casbin` in `configure()`, but the package isn't installed.
250
253
 
251
- **Fix:** Install casbin as a dependency:
254
+ **Fix:**
252
255
 
253
256
  ```bash
254
257
  bun add casbin
255
258
  ```
256
259
 
257
- ### "[CasbinAuthorizationEnforcer] options.model is required"
260
+ ### "[CasbinAuthorizationEnforcer] options.model is required."
258
261
 
259
- **Cause:** The Casbin enforcer's options do not include a `model` field. The model defines the casbin RBAC/ABAC rules structure.
262
+ **Cause:** The enforcer options have no `model`.
260
263
 
261
- **Fix:** Provide `model` in the enforcer options:
264
+ **Fix:** Provide a model - inline text (recommended: `CASBIN_RBAC_DOMAIN_SCOPED_MODEL`) or a file path.
262
265
 
263
266
  ```typescript
264
- AuthorizationEnforcerRegistry.getInstance().register({
265
- container: this,
266
- enforcers: [{
267
- enforcer: CasbinAuthorizationEnforcer,
268
- name: 'casbin',
269
- type: 'casbin',
270
- options: {
271
- model: {
272
- driver: 'file',
273
- definition: path.resolve(__dirname, './security/model.conf'),
274
- },
275
- cached: { use: false },
276
- },
277
- }],
278
- });
267
+ options: {
268
+ model: { driver: CasbinEnforcerModelDrivers.TEXT, definition: CASBIN_RBAC_DOMAIN_SCOPED_MODEL },
269
+ isScoped: true,
270
+ adapter,
271
+ cached: { use: false },
272
+ }
279
273
  ```
280
274
 
281
- ### "[extractUserLines] Adapter does not support loadFilteredPolicy"
275
+ ### "[registerMatchers] Role definition "g2" is not declared in the Casbin model..."
282
276
 
283
- **Cause:** The adapter provided to the Casbin enforcer does not implement the `loadFilteredPolicy` method from casbin's `FilteredAdapter` interface. The authorization system always uses filtered policy loading.
277
+ **Cause:** `domainMatching.roleDefinition` references a relation the model doesn't declare under `[role_definition]`. Without this check, Casbin would register the function as a silent no-op, leaving wildcard domains permanently unmatched - global roles would be silently denied. The enforcer throws at boot instead.
284
278
 
285
- **Fix:** Use an adapter that implements `FilteredAdapter`, such as `ScopedCasbinAdapter` or a custom adapter extending `BaseFilteredAdapter`:
279
+ **Fix:** Point `roleDefinition` at a relation the model actually declares.
286
280
 
287
281
  ```typescript
288
- import { ScopedCasbinAdapter } from '@venizia/ignis';
289
-
290
- const adapter = new ScopedCasbinAdapter({
291
- dataSource,
292
- entities: { /* IScopedCasbinEntities */ },
293
- });
282
+ domainMatching: { roleDefinition: 'g', fn: CasbinDomainMatchingFunctions.KEY_MATCH } // model must declare `g = _, _, _`
294
283
  ```
295
284
 
285
+ ### "[CasbinAuthorizationEnforcer] Matcher smoke test failed at warmup..."
286
+
287
+ **Cause:** `assertMatcherCompilesSync()` runs a dummy `enforceSync()` at warmup to force casbin's lazy matcher compile. It failed for one of these reasons:
288
+
289
+ - A matcher syntax error.
290
+ - A function referenced in the matcher that was never registered.
291
+ - A request-arity mismatch (3 vs. 4 tokens).
292
+
293
+ **Fix:** Check the `[matchers]` section of your `.conf`.
294
+
295
+ - Scoped model: confirm `isScoped: true` is set. It registers `objectMatch` and `keyMatch` automatically.
296
+ - Custom flat model: confirm every function the matcher calls is registered via `domainMatching`.
297
+
296
298
  ### "[CasbinAuthorizationEnforcer] cached.options.expiresIn must be >= 10000"
297
299
 
298
- **Cause:** The `expiresIn` value for the cache TTL is too small. Minimum is 10,000 ms (10 seconds), enforced by the `MIN_EXPIRES_IN` constant.
300
+ **Cause:** `expiresIn` is below the 10,000 ms minimum (`MIN_EXPIRES_IN`).
299
301
 
300
- **Fix:** Increase the expiration time:
302
+ **Fix:**
301
303
 
302
304
  ```typescript
303
305
  cached: {
@@ -305,53 +307,42 @@ cached: {
305
307
  driver: 'redis',
306
308
  options: {
307
309
  connection: redisHelper,
308
- expiresIn: 5 * 60 * 1000, // 5 minutes (minimum: 10,000 ms)
309
- keyFn: ({ user }) => `authz:policies:${user.userId}`,
310
+ expiresIn: 5 * 60 * 1000, // 5 minutes (minimum: 10,000 ms)
311
+ keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
310
312
  },
311
313
  },
312
314
  ```
313
315
 
314
316
  ### "[CasbinAuthorizationEnforcer] Not configured. Call configure() first."
315
317
 
316
- **Cause:** The Casbin enforcer's `evaluate()` (or `loadPolicyLinesIntoModel()`) was called before `configure()` built the enforcer pool. This should not happen when using `resolveEnforcer()` (which auto-configures), but can occur if the enforcer is resolved manually via the DI container.
318
+ **Cause:** `evaluate()` (or its internal `loadPolicyLinesIntoModel()`) ran before `configure()` built the enforcer pool. This shouldn't happen through `resolveEnforcer()`, which auto-configures. It can occur if the enforcer is resolved manually from the DI container.
317
319
 
318
- **Fix:** Always resolve enforcers through the registry's `resolveEnforcer()` method, which handles configure-once automatically:
320
+ **Fix:** Always go through the registry, which handles configure-once automatically.
319
321
 
320
322
  ```typescript
321
323
  const enforcer = await AuthorizationEnforcerRegistry.getInstance().resolveEnforcer({ name: 'casbin' });
322
- // configure() is called automatically on first resolve
323
324
  ```
324
325
 
325
- ### "[loadPoliciesWithRedisCache] Invalid cachedKey"
326
+ ### "[CasbinAuthorizationEnforcer] keyFn returned an empty cache key."
326
327
 
327
- **Cause:** The `keyFn` in Redis cache options returned a falsy value (empty string, null, undefined). The cache key is required to store/retrieve policies from Redis.
328
+ **Cause:** `cached.options.keyFn` returned a falsy value for a user.
328
329
 
329
- **Fix:** Ensure your `keyFn` always returns a non-empty string:
330
+ **Fix:** Always return a stable, non-empty key.
330
331
 
331
332
  ```typescript
332
- keyFn: ({ user }) => {
333
- if (!user.userId) {
334
- throw new Error('User ID is required for cache key');
335
- }
336
- return `authz:policies:${user.userId}`;
337
- },
333
+ keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
338
334
  ```
339
335
 
340
- ### "[CasbinAuthorizationEnforcer] Not configured. Call configure() first."
341
-
342
- **Cause:** `evaluate()` or `loadPolicyLinesIntoModel()` ran before the enforcer pool was built.
343
-
344
- **Fix:** Ensure `configure()` completed successfully (the registry calls it on first use) before any
345
- evaluation. The enforcer pool is created in `configure()`.
346
-
347
- ### "[CasbinAuthorizationEnforcer] keyFn returned an empty cache key."
336
+ ### "[extractUserLines] Adapter does not support loadFilteredPolicy"
348
337
 
349
- **Cause:** The Redis `cached.options.keyFn` returned an empty string for a user.
338
+ **Cause:** `options.adapter` doesn't implement casbin's `FilteredAdapter.loadFilteredPolicy`. Authorization always loads policies filtered by principal.
350
339
 
351
- **Fix:** Return a stable, non-empty key (commonly derived from `user.principalType` + `user.userId`):
340
+ **Fix:** Use `ScopedCasbinAdapter`, or extend `BaseFilteredAdapter` and implement `loadFilteredPolicy`.
352
341
 
353
342
  ```typescript
354
- keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
343
+ import { ScopedCasbinAdapter } from '@venizia/ignis';
344
+
345
+ const adapter = new ScopedCasbinAdapter({ dataSource, entities: { /* IScopedCasbinEntities */ } });
355
346
  ```
356
347
 
357
348
  ### "[resolveModel] Invalid model.driver | Valids: [file, text]"
@@ -360,39 +351,44 @@ keyFn: ({ user }) => `authz:policies:${user.principalType}:${user.userId}`,
360
351
 
361
352
  **Fix:** Use `CasbinEnforcerModelDrivers.FILE` (`'file'`) or `CasbinEnforcerModelDrivers.TEXT` (`'text'`).
362
353
 
363
- > There is no longer an `Invalid cached.driver` runtime error - `cached` is a typed union, so an
364
- > unsupported cache driver is caught at compile time. Caching is **Redis-only**.
354
+ ### "[CasbinAuthorizationEnforcer] Cache management requires the redis cache driver, but caching is disabled."
355
+
356
+ **Cause:** `invalidateUserCache()` or `rebuildUserCache()` was called on an enforcer configured with `cached: { use: false }`.
357
+
358
+ **Fix:** Either configure Redis caching, or don't call the cache-management methods for a non-cached enforcer.
365
359
 
366
360
  ## Common Patterns
367
361
 
368
- ### Authorization Is Not Running
362
+ ### Authorization is not running
363
+
364
+ Check, in order:
365
+
366
+ 1. `authorize` is actually set on the route config (not just `authenticate`).
367
+ 2. `authenticate` isn't `{ skip: true }` - that skips authorization too, in both raw route configs and the CRUD factory.
368
+ 3. The component is registered AND at least one enforcer is registered via `AuthorizationEnforcerRegistry`.
369
+ 4. If no enforcers are registered, the middleware denies with a 403 naming the missing enforcer - unless `defaultDecision: 'allow'` is set, in which case it proceeds and logs a warning. A warning in your logs on every request is often the real sign of "no enforcer registered" during rollout.
369
370
 
370
- Check middleware injection order:
371
- 1. Verify `authorize` is set on the route config (not just `authenticate`)
372
- 2. Verify `authenticate` is not set to `{ skip: true }` (which also skips authorization in CRUD factory)
373
- 3. Verify the component is registered and enforcers are registered via the registry
374
- 4. Note: if no enforcers are registered, the middleware skips authorization silently (calls `next()`) rather than throwing an error
371
+ ### Rules are rebuilt on every request
375
372
 
376
- ### Rules Are Built On Every Request
373
+ Rules cache on `Authorization.RULES`, but only **within one request**:
377
374
 
378
- Check that rules are being cached correctly. The middleware caches on `Authorization.RULES`:
379
- - Cached per-request (each new request starts fresh)
380
- - Multiple authorization specs on the same route share the cache
381
- - If rules are `undefined` or `null`, they will be rebuilt
375
+ - A new HTTP request always starts with an empty cache.
376
+ - Multiple `authorize` specs on the same route share the cache - only the first builds.
377
+ - `undefined`/`null` on `Authorization.RULES` triggers a rebuild - `c.set(Authorization.RULES, null)` forces one mid-request.
382
378
 
383
- ### Casbin Policies Not Loading
379
+ ### Casbin policies not loading
384
380
 
385
- 1. **Check adapter entities** -- ensure `IScopedCasbinEntities` (`policyDefinition`/`permission` table names + `schemaName`, `principals`, `domainTypes`) match your database schema
386
- 2. **Check the variant column** -- `PolicyDefinition.variant` must use the `AuthorizationPolicyVariants.*.action` values: `grant`, `assign_role`, `join_domain`, `role_inherits`, `resource_inherits`, `action_inherits`, `domain_inherits`
387
- 3. **Check subject/target types** -- the SQL queries filter by `subject_type`/`target_type` against `principals` and `domainTypes`
388
- 4. **Check the model** -- for scoped RBAC, use `CASBIN_RBAC_DOMAIN_SCOPED_MODEL` with `isScoped: true`
381
+ 1. **Adapter entities** - `IScopedCasbinEntities` (`policyDefinition`/`permission` table + schema names, `principals`, `domainTypes`) must match your actual database schema.
382
+ 2. **Variant column** - `PolicyDefinition.variant` must use `AuthorizationPolicyVariants.*.action` values: `grant`, `assign_role`, `join_domain`, `role_inherits`, `resource_inherits`, `action_inherits`, `domain_inherits`.
383
+ 3. **Subject/target types** - the adapter's SQL filters by `subject_type`/`target_type` against `principals` and `domainTypes`; a mismatch silently returns zero rows.
384
+ 4. **Model** - scoped RBAC needs `CASBIN_RBAC_DOMAIN_SCOPED_MODEL` with `isScoped: true` together; one without the other misfires.
389
385
 
390
- ### Redis Cache Not Working
386
+ ### Redis cache not working
391
387
 
392
- 1. **Check Redis connection** -- verify the `AbstractRedisHelper` subclass (`RedisSingleHelper`, `RedisClusterHelper`, or `RedisSentinelHelper`) is properly connected
393
- 2. **Check keyFn** -- ensure it returns a unique, non-empty key per user
394
- 3. **Check expiresIn** -- must be >= 10,000 ms (`MIN_EXPIRES_IN`)
395
- 4. **Verify cache hit** -- check logs for `"Loaded CACHED Policies"` vs `"Loaded ADAPTER + CACHED Policies"`
388
+ 1. **Connection** - verify the `IRedisHelper` (`RedisSingleHelper`/`RedisClusterHelper`/`RedisSentinelHelper`) is actually connected.
389
+ 2. **`keyFn`** - must return a unique, non-empty key per user.
390
+ 3. **`expiresIn`** - must be `>= 10_000` (`MIN_EXPIRES_IN`).
391
+ 4. **Corruption is silent.** A malformed cached payload is discarded and refetched (see the Casbin runtime error table above). If caching never seems to hit, suspect a `keyFn` mismatch before corruption.
396
392
 
397
393
  ## See Also
398
394