@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.
- package/README.md +400 -216
- package/dist/index.cjs +187 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -14
- package/dist/index.d.ts +65 -14
- package/dist/index.js +187 -91
- package/dist/index.js.map +1 -1
- package/docs/README.md +89 -0
- package/docs/SUMMARY.md +34 -0
- package/docs/installation/GettingStarted.md +474 -0
- package/docs/reference/Configuration.md +198 -0
- package/docs/reference/TypesAndInterfaces.md +373 -0
- package/docs/usage/BundleLifecycle.md +209 -0
- package/docs/usage/ConditionalRules.md +251 -0
- package/docs/usage/ContextPolicy.md +196 -0
- package/docs/usage/CustomEffect.md +217 -0
- package/docs/usage/DecisionTrace.md +289 -0
- package/docs/usage/Effects.md +213 -0
- package/docs/usage/Evaluate.md +164 -0
- package/docs/usage/PolicyDefaults.md +220 -0
- package/package.json +3 -5
|
@@ -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.
|
|
4
|
-
"description": "Govplane Runtime SDK (Node/TS)
|
|
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
|
}
|