@govplane/runtime-sdk 0.2.4
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 +365 -0
- package/dist/index.cjs +1080 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +413 -0
- package/dist/index.d.ts +413 -0
- package/dist/index.js +1054 -0
- package/dist/index.js.map +1 -0
- package/docs/operations/Govplane_Incident_Playbook.md +318 -0
- package/docs/operations/Govplane_Runtime_Incident_Controls.md +101 -0
- package/docs/security/Govplane_Threat_Model.md +137 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# @govplane/runtime-sdk
|
|
2
|
+
|
|
3
|
+
Official **Govplane Runtime SDK** for Node.js.
|
|
4
|
+
|
|
5
|
+
The Govplane Runtime SDK is a **secure, production-grade client and policy engine** designed to consume **precompiled runtime governance bundles** and evaluate decisions **locally**, with **zero PII**, no exposed endpoints, and minimal operational risk.
|
|
6
|
+
|
|
7
|
+
It is intended for backend services, workers, gateways, and critical paths that must react to policy changes in near real-time without delegating decisions to a remote service.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Design Principles
|
|
12
|
+
|
|
13
|
+
- ๐ **No exposed endpoints** โ no middleware, no inbound surface
|
|
14
|
+
- ๐ง **Local-first evaluation** โ decisions are made in-process
|
|
15
|
+
- ๐งผ **Zero PII by design** โ strict context validation and allowlists
|
|
16
|
+
- ๐ฆ **Precompiled policies** โ no DSL or dynamic code at runtime
|
|
17
|
+
- ๐งฉ **Deterministic outcomes** โ same input, same decision
|
|
18
|
+
- โก **Cheap polling** โ HEAD + ETag + conditional GET
|
|
19
|
+
- ๐งฏ **Failure-safe** โ backoff, degraded mode, deny-by-default
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Key Capabilities
|
|
24
|
+
|
|
25
|
+
### Runtime Client
|
|
26
|
+
- Efficient **HEAD-first polling** using `ETag` and `If-None-Match`
|
|
27
|
+
- In-memory bundle cache
|
|
28
|
+
- Automatic request de-duplication
|
|
29
|
+
- Configurable polling interval
|
|
30
|
+
- **Burst mode** for incident response
|
|
31
|
+
- **Exponential backoff with jitter**
|
|
32
|
+
- Automatic **degraded state**
|
|
33
|
+
- Status subscriptions (`ok` / `degraded`)
|
|
34
|
+
|
|
35
|
+
### Policy Engine (SDK-only)
|
|
36
|
+
- Supports:
|
|
37
|
+
- `allow`
|
|
38
|
+
- `deny`
|
|
39
|
+
- `kill_switch`
|
|
40
|
+
- `throttle`
|
|
41
|
+
- **Deny-by-default**
|
|
42
|
+
- Kill-switch always wins
|
|
43
|
+
- Throttle selects the **most restrictive rule**
|
|
44
|
+
- Deterministic rule ordering
|
|
45
|
+
- Precompiled `when` AST evaluation
|
|
46
|
+
- No dynamic evaluation or code execution
|
|
47
|
+
|
|
48
|
+
### Security & Context Safety
|
|
49
|
+
- Explicit context allowlist
|
|
50
|
+
- Unknown keys rejected (PII protection)
|
|
51
|
+
- Configurable limits:
|
|
52
|
+
- max string length
|
|
53
|
+
- max array length
|
|
54
|
+
- max depth
|
|
55
|
+
- Context policy is fully configurable per engine
|
|
56
|
+
|
|
57
|
+
### Decision Trace / Explain (SDK-only)
|
|
58
|
+
- Optional decision trace generation
|
|
59
|
+
- Compact or full trace modes
|
|
60
|
+
- Sampling support (e.g. 1% in production)
|
|
61
|
+
- Force tracing for debugging
|
|
62
|
+
- Structured JSON trace output
|
|
63
|
+
- **Async trace sinks** (fire-and-forget)
|
|
64
|
+
- Bounded queues with drop strategies
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Requirements
|
|
69
|
+
|
|
70
|
+
- **Node.js โฅ 18**
|
|
71
|
+
- A valid **Govplane Runtime Key**
|
|
72
|
+
- Access to a Govplane Runtime API (`gp-runtime`)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install @govplane/runtime-sdk
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### 1. Create a Runtime Client
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { RuntimeClient } from "@govplane/runtime-sdk";
|
|
90
|
+
|
|
91
|
+
const client = new RuntimeClient({
|
|
92
|
+
baseUrl: "https://runtime.govplane.com",
|
|
93
|
+
runtimeKey: process.env.GP_RUNTIME_KEY!,
|
|
94
|
+
orgId: "org_dev",
|
|
95
|
+
projectId: "prj_web",
|
|
96
|
+
env: "prod"
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
client.start();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 2. Create a Policy Engine
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { createPolicyEngine } from "@govplane/runtime-sdk";
|
|
106
|
+
|
|
107
|
+
const engine = createPolicyEngine({
|
|
108
|
+
getBundle: () => client.getCached().bundle,
|
|
109
|
+
validateContext: true,
|
|
110
|
+
contextPolicy: {
|
|
111
|
+
allowedKeys: [
|
|
112
|
+
"ctx.plan",
|
|
113
|
+
"ctx.country",
|
|
114
|
+
"ctx.amount",
|
|
115
|
+
"ctx.isAuthenticated"
|
|
116
|
+
],
|
|
117
|
+
maxStringLen: 64,
|
|
118
|
+
maxArrayLen: 10
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. Evaluate a Decision
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const result = engine.evaluate({
|
|
127
|
+
target: {
|
|
128
|
+
service: "payments",
|
|
129
|
+
resource: "checkout",
|
|
130
|
+
action: "create"
|
|
131
|
+
},
|
|
132
|
+
context: {
|
|
133
|
+
plan: "pro",
|
|
134
|
+
country: "ES",
|
|
135
|
+
amount: 42,
|
|
136
|
+
isAuthenticated: true
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
console.log(result.decision); // allow | deny | throttle | kill_switch
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Runtime Bundle Model
|
|
146
|
+
|
|
147
|
+
The SDK retrieves **materialized runtime bundles** generated by Govplane.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
type RuntimeBundleV1 = {
|
|
151
|
+
schemaVersion: 1;
|
|
152
|
+
orgId: string;
|
|
153
|
+
projectId: string;
|
|
154
|
+
env: string;
|
|
155
|
+
generatedAt: string;
|
|
156
|
+
policies: unknown[];
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The bundle is treated as **immutable and read-only**.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Polling & Burst Mode
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
// Force immediate refresh
|
|
168
|
+
await client.refreshNow();
|
|
169
|
+
|
|
170
|
+
// Temporarily increase polling frequency (e.g. incident response)
|
|
171
|
+
await client.refreshNow({ burst: true });
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Polling strategy:
|
|
175
|
+
1. `HEAD /v1/runtime/bundle`
|
|
176
|
+
2. Compare `ETag`
|
|
177
|
+
3. If changed โ `GET`
|
|
178
|
+
4. If unchanged โ no download, no JSON parse
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Backoff & Degraded Mode
|
|
183
|
+
|
|
184
|
+
On repeated failures, the client:
|
|
185
|
+
- Applies exponential backoff with jitter
|
|
186
|
+
- Enters **degraded** state after N failures
|
|
187
|
+
- Emits status updates
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
client.onStatus((status) => {
|
|
191
|
+
if (status.state === "degraded") {
|
|
192
|
+
console.warn("Runtime degraded:", status);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
In degraded mode, cached bundles remain active.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Runtime Incident Controls
|
|
202
|
+
|
|
203
|
+
During an incident (DDoS, fraud spike, abuse, misconfiguration), it may be necessary to:
|
|
204
|
+
- Force faster policy propagation
|
|
205
|
+
- Trigger immediate policy refresh
|
|
206
|
+
- Temporarily increase polling frequency (โburst modeโ)
|
|
207
|
+
|
|
208
|
+
Govplane Runtime SDK provides **passive incident controls** that allow operators to react to incidents **without exposing endpoints**, **without handling PII**, and **without recompiling application code**.
|
|
209
|
+
|
|
210
|
+
These controls are designed to be:
|
|
211
|
+
- Local-only
|
|
212
|
+
- Zero-network-surface
|
|
213
|
+
- Safe under active attack
|
|
214
|
+
- Compatible with containerized and orchestrated environments
|
|
215
|
+
|
|
216
|
+
### Supported Incident Control Mechanisms
|
|
217
|
+
|
|
218
|
+
| Mechanism | Restart Required | Network Surface | Hot | Recommended |
|
|
219
|
+
|---------|------------------|-----------------|-----|-------------|
|
|
220
|
+
| Environment Variable | Usually yes | None | โ | โ
|
|
|
221
|
+
| File-based Hot Reload | No | None | โ
| โญ **Primary** |
|
|
222
|
+
| POSIX Signal (SIGUSR1) | No | None | โ
| Optional |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
**Example: Environment Variable Incident Mode**
|
|
227
|
+
|
|
228
|
+
If the following environment variable is set, the Runtime SDK automatically enters **incident mode**:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
GP_RUNTIME_INCIDENT=1
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
When detected:
|
|
235
|
+
โข Burst polling is enabled
|
|
236
|
+
โข Refresh cadence increases
|
|
237
|
+
โข Degraded recovery is accelerated
|
|
238
|
+
|
|
239
|
+
Notes
|
|
240
|
+
โข In most container platforms (ECS, Kubernetes), environment variables cannot be changed at runtime
|
|
241
|
+
โข A rolling restart is usually required
|
|
242
|
+
โข For zero-restart response, use File-based Hot Reload
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
**More details: [Govplane Runtime SDK โ Runtime Incident Controls](docs/operations/Govplane_Runtime_Incident_Controls.md)**
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Throttle Decisions
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
if (result.decision === "throttle") {
|
|
253
|
+
rateLimiter.apply(
|
|
254
|
+
result.throttle.key,
|
|
255
|
+
result.throttle.limit,
|
|
256
|
+
result.throttle.windowSeconds
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
When multiple throttle rules match, the **most restrictive** one is selected.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Decision Trace / Explain
|
|
266
|
+
|
|
267
|
+
### Production (Sampled)
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
const out = engine.evaluateWithTrace(
|
|
271
|
+
{ target, context },
|
|
272
|
+
{ sampling: 0.01, mode: "compact" }
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
if (out.trace) {
|
|
276
|
+
console.log(out.trace.summary);
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Debug (Forced)
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
engine.evaluateWithTrace(
|
|
284
|
+
{ target, context },
|
|
285
|
+
{ force: true, mode: "full" }
|
|
286
|
+
);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Trace Guarantees
|
|
290
|
+
|
|
291
|
+
- No rule bodies
|
|
292
|
+
- No context values
|
|
293
|
+
- No PII
|
|
294
|
+
- Deterministic structure
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Async Trace Sink
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
engine.setTraceSink(
|
|
302
|
+
createAsyncTraceSink({
|
|
303
|
+
maxQueue: 100,
|
|
304
|
+
drop: "drop_new",
|
|
305
|
+
sink: async (event) => {
|
|
306
|
+
await fetch("https://trace-endpoint", {
|
|
307
|
+
method: "POST",
|
|
308
|
+
body: JSON.stringify(event)
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
})
|
|
312
|
+
);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The sink:
|
|
316
|
+
- Never blocks evaluation
|
|
317
|
+
- Never throws
|
|
318
|
+
- Drops safely under pressure
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## What This SDK Does NOT Do
|
|
323
|
+
|
|
324
|
+
- โ No HTTP middleware
|
|
325
|
+
- โ No inbound endpoints
|
|
326
|
+
- โ No request interception
|
|
327
|
+
- โ No PII handling
|
|
328
|
+
- โ No policy authoring
|
|
329
|
+
- โ No dynamic code execution
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Security Notes
|
|
334
|
+
|
|
335
|
+
- Runtime keys are **read-only**
|
|
336
|
+
- Keys are scoped by org / project / env
|
|
337
|
+
- Bundles are immutable
|
|
338
|
+
- SDK cannot modify state
|
|
339
|
+
- Safe to embed in critical paths
|
|
340
|
+
|
|
341
|
+
### Threat Model
|
|
342
|
+
|
|
343
|
+
Please refer to the [Govplane Runtime SDK Threat Model & Security Guarantees](docs/security/Govplane_Threat_Model.md) for a detailed analysis.
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Performance Notes
|
|
348
|
+
|
|
349
|
+
- Designed for second-level polling
|
|
350
|
+
- Suitable for APIs, workers, gateways
|
|
351
|
+
- Not intended for browser usage
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Versioning & Compatibility
|
|
356
|
+
|
|
357
|
+
- Bundles are versioned (`schemaVersion`)
|
|
358
|
+
- Current version: `RuntimeBundleV1`
|
|
359
|
+
- Future versions will be backward-compatible
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
363
|
+
## License
|
|
364
|
+
|
|
365
|
+
MIT ยฉ Govplane
|