@govplane/runtime-sdk 0.2.4 → 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.
@@ -0,0 +1,220 @@
1
+ ---
2
+ description: >-
3
+ Configure a fallback effect at the policy level that fires when no rule
4
+ matches a given target.
5
+ ---
6
+
7
+ # Policy Defaults
8
+
9
+ ## Overview
10
+
11
+ Every policy can declare a `defaults` object. When **no rule within that policy produces a match** for the evaluated target, the engine applies the policy default as a lowest-priority synthetic decision.
12
+
13
+ This removes the need for catch-all rules and makes deny-by-default, open-allow, or custom-fallback policies cleaner to express.
14
+
15
+ ---
16
+
17
+ ## How it works
18
+
19
+ Policy defaults are internally treated as a synthetic rule with `priority = -1`. They are only considered when **zero rules** in the same policy matched the target (i.e. either no rule targeted that service/resource/action, or every candidate rule was disabled or had a `when_false` result with no `elseEffect`).
20
+
21
+ Because the priority is `-1`, any explicit rule — including `when: false` cases where `elseEffect` is defined — will always win over a policy default.
22
+
23
+ ---
24
+
25
+ ## Bundle shape
26
+
27
+ ```json
28
+ {
29
+ "policyKey": "my-policy",
30
+ "activeVersion": 5,
31
+ "defaults": {
32
+ "effect": "<effect-type>",
33
+ ...effect-specific fields...
34
+ },
35
+ "rules": [...]
36
+ }
37
+ ```
38
+
39
+ ---
40
+
41
+ ## All supported default effect types
42
+
43
+ ### `allow`
44
+
45
+ All unmatched targets are allowed.
46
+
47
+ ```json
48
+ {
49
+ "policyKey": "open-api",
50
+ "defaults": { "effect": "allow" },
51
+ "rules": [
52
+ {
53
+ "id": "r_block_admin",
54
+ "status": "active",
55
+ "priority": 10,
56
+ "target": { "service": "api", "resource": "admin", "action": "delete" },
57
+ "effect": { "type": "deny" }
58
+ }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ `admin/delete` → `deny` (matched rule).
64
+ Everything else → `allow` (policy default).
65
+
66
+ ---
67
+
68
+ ### `deny`
69
+
70
+ All unmatched targets are denied. This is the most common pattern for strict allow-lists.
71
+
72
+ ```json
73
+ {
74
+ "policyKey": "strict-rbac",
75
+ "defaults": { "effect": "deny" },
76
+ "rules": [
77
+ {
78
+ "id": "r_allow_admins",
79
+ "status": "active",
80
+ "priority": 10,
81
+ "target": { "service": "control", "resource": "settings", "action": "write" },
82
+ "when": { "op": "in", "path": "role", "values": ["admin", "superuser"] },
83
+ "thenEffect": { "type": "allow" },
84
+ "effect": { "type": "deny" }
85
+ }
86
+ ]
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ### `throttle`
93
+
94
+ All unmatched targets are throttled.
95
+
96
+ ```json
97
+ {
98
+ "policyKey": "rate-limited-api",
99
+ "defaults": {
100
+ "effect": "throttle",
101
+ "throttle": { "limit": 100, "windowSeconds": 60, "key": "tenant" }
102
+ },
103
+ "rules": []
104
+ }
105
+ ```
106
+
107
+ Decision when no rules match:
108
+
109
+ ```typescript
110
+ {
111
+ decision: "throttle",
112
+ reason: "default",
113
+ policyKey: "rate-limited-api",
114
+ throttle: { limit: 100, windowSeconds: 60, key: "tenant" }
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ### `kill_switch`
121
+
122
+ All targets are immediately blocked — useful as an emergency circuit breaker pushed via bundle without a code deploy.
123
+
124
+ ```json
125
+ {
126
+ "policyKey": "payments-circuit-breaker",
127
+ "defaults": {
128
+ "effect": "kill_switch",
129
+ "killSwitch": { "service": "payments", "reason": "DB degradation — INC-4421" }
130
+ },
131
+ "rules": []
132
+ }
133
+ ```
134
+
135
+ Decision:
136
+
137
+ ```typescript
138
+ {
139
+ decision: "kill_switch",
140
+ reason: "default",
141
+ policyKey: "payments-circuit-breaker",
142
+ killSwitch: { service: "payments", reason: "DB degradation — INC-4421" }
143
+ }
144
+ ```
145
+
146
+ ---
147
+
148
+ ### `custom`
149
+
150
+ All unmatched targets return a custom string value. Useful for returning default feature-flag states.
151
+
152
+ ```json
153
+ {
154
+ "policyKey": "feature-flags",
155
+ "activeVersion": 7,
156
+ "defaults": {
157
+ "effect": "custom",
158
+ "customEffect": "{\"newDashboard\": false, \"aiSearch\": false}"
159
+ },
160
+ "rules": [
161
+ {
162
+ "id": "r_flags_enterprise",
163
+ "status": "active",
164
+ "priority": 10,
165
+ "target": { "service": "app", "resource": "features", "action": "read" },
166
+ "when": { "op": "eq", "path": "plan", "value": "enterprise" },
167
+ "thenEffect": {
168
+ "type": "custom",
169
+ "value": "{\"newDashboard\": true, \"aiSearch\": true}"
170
+ },
171
+ "effect": { "type": "deny" }
172
+ }
173
+ ]
174
+ }
175
+ ```
176
+
177
+ Enterprise users on the target → `custom` via rule.
178
+ All other users → `custom` via policy default (the `false` flags).
179
+ Unrelated targets (different service/resource/action) → policy default (`custom` with `false` flags).
180
+
181
+ ---
182
+
183
+ ## Decision fields when `reason === "default"`
184
+
185
+ When a decision is produced by a policy default, the following applies:
186
+
187
+ | Field | Value |
188
+ |---|---|
189
+ | `reason` | `"default"` |
190
+ | `policyKey` | The policy whose default fired |
191
+ | `ruleId` | `undefined` — no rule was involved |
192
+
193
+ ```typescript
194
+ if (result.reason === "default") {
195
+ // fired from policy default or SDK's global deny-by-default
196
+ metrics.increment("decisions.default", { policy: result.policyKey ?? "global" });
197
+ }
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Global deny-by-default
203
+
204
+ If **no policy has a matching rule or default** for the evaluated target, the SDK returns:
205
+
206
+ ```typescript
207
+ { decision: "deny", reason: "default" } // policyKey is undefined
208
+ ```
209
+
210
+ This is the SDK's last-resort fallback and requires no configuration.
211
+
212
+ ---
213
+
214
+ {% content-ref url="Effects.md" %}
215
+ [Effect Types](Effects.md)
216
+ {% endcontent-ref %}
217
+
218
+ {% content-ref url="ConditionalRules.md" %}
219
+ [Conditional Rules](ConditionalRules.md)
220
+ {% endcontent-ref %}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@govplane/runtime-sdk",
3
- "version": "0.2.4",
4
- "description": "Govplane Runtime SDK (Node/TS) with ETag caching + polling",
3
+ "version": "0.5.0",
4
+ "description": "Govplane Runtime SDK (Node/TS)",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.cjs",
@@ -34,7 +34,5 @@
34
34
  "tsup": "^8.2.4",
35
35
  "typescript": "^5.6.3"
36
36
  },
37
- "dependencies": {
38
- "undici": "^7.18.2"
39
- }
37
+ "dependencies": {}
40
38
  }