usertrust 0.1.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.
Files changed (149) hide show
  1. package/dist/audit/canonical.d.ts +7 -0
  2. package/dist/audit/canonical.d.ts.map +1 -0
  3. package/dist/audit/canonical.js +24 -0
  4. package/dist/audit/canonical.js.map +1 -0
  5. package/dist/audit/chain.d.ts +33 -0
  6. package/dist/audit/chain.d.ts.map +1 -0
  7. package/dist/audit/chain.js +285 -0
  8. package/dist/audit/chain.js.map +1 -0
  9. package/dist/audit/entropy.d.ts +95 -0
  10. package/dist/audit/entropy.d.ts.map +1 -0
  11. package/dist/audit/entropy.js +229 -0
  12. package/dist/audit/entropy.js.map +1 -0
  13. package/dist/audit/merkle.d.ts +87 -0
  14. package/dist/audit/merkle.d.ts.map +1 -0
  15. package/dist/audit/merkle.js +315 -0
  16. package/dist/audit/merkle.js.map +1 -0
  17. package/dist/audit/rotation.d.ts +61 -0
  18. package/dist/audit/rotation.d.ts.map +1 -0
  19. package/dist/audit/rotation.js +160 -0
  20. package/dist/audit/rotation.js.map +1 -0
  21. package/dist/audit/verify.d.ts +20 -0
  22. package/dist/audit/verify.d.ts.map +1 -0
  23. package/dist/audit/verify.js +73 -0
  24. package/dist/audit/verify.js.map +1 -0
  25. package/dist/board/board.d.ts +67 -0
  26. package/dist/board/board.d.ts.map +1 -0
  27. package/dist/board/board.js +191 -0
  28. package/dist/board/board.js.map +1 -0
  29. package/dist/board/concerns.d.ts +59 -0
  30. package/dist/board/concerns.d.ts.map +1 -0
  31. package/dist/board/concerns.js +149 -0
  32. package/dist/board/concerns.js.map +1 -0
  33. package/dist/board/director.d.ts +49 -0
  34. package/dist/board/director.d.ts.map +1 -0
  35. package/dist/board/director.js +127 -0
  36. package/dist/board/director.js.map +1 -0
  37. package/dist/cli/health.d.ts +8 -0
  38. package/dist/cli/health.d.ts.map +1 -0
  39. package/dist/cli/health.js +119 -0
  40. package/dist/cli/health.js.map +1 -0
  41. package/dist/cli/init.d.ts +8 -0
  42. package/dist/cli/init.d.ts.map +1 -0
  43. package/dist/cli/init.js +67 -0
  44. package/dist/cli/init.js.map +1 -0
  45. package/dist/cli/inspect.d.ts +8 -0
  46. package/dist/cli/inspect.d.ts.map +1 -0
  47. package/dist/cli/inspect.js +114 -0
  48. package/dist/cli/inspect.js.map +1 -0
  49. package/dist/cli/main.d.ts +3 -0
  50. package/dist/cli/main.d.ts.map +1 -0
  51. package/dist/cli/main.js +35 -0
  52. package/dist/cli/main.js.map +1 -0
  53. package/dist/cli/snapshot.d.ts +10 -0
  54. package/dist/cli/snapshot.d.ts.map +1 -0
  55. package/dist/cli/snapshot.js +61 -0
  56. package/dist/cli/snapshot.js.map +1 -0
  57. package/dist/cli/tb.d.ts +8 -0
  58. package/dist/cli/tb.d.ts.map +1 -0
  59. package/dist/cli/tb.js +43 -0
  60. package/dist/cli/tb.js.map +1 -0
  61. package/dist/cli/verify.d.ts +7 -0
  62. package/dist/cli/verify.d.ts.map +1 -0
  63. package/dist/cli/verify.js +32 -0
  64. package/dist/cli/verify.js.map +1 -0
  65. package/dist/config.d.ts +12 -0
  66. package/dist/config.d.ts.map +1 -0
  67. package/dist/config.js +34 -0
  68. package/dist/config.js.map +1 -0
  69. package/dist/detect.d.ts +18 -0
  70. package/dist/detect.d.ts.map +1 -0
  71. package/dist/detect.js +49 -0
  72. package/dist/detect.js.map +1 -0
  73. package/dist/govern.d.ts +75 -0
  74. package/dist/govern.d.ts.map +1 -0
  75. package/dist/govern.js +581 -0
  76. package/dist/govern.js.map +1 -0
  77. package/dist/index.d.ts +6 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +8 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/ledger/client.d.ts +89 -0
  82. package/dist/ledger/client.d.ts.map +1 -0
  83. package/dist/ledger/client.js +417 -0
  84. package/dist/ledger/client.js.map +1 -0
  85. package/dist/ledger/engine.d.ts +68 -0
  86. package/dist/ledger/engine.d.ts.map +1 -0
  87. package/dist/ledger/engine.js +142 -0
  88. package/dist/ledger/engine.js.map +1 -0
  89. package/dist/ledger/pricing.d.ts +35 -0
  90. package/dist/ledger/pricing.d.ts.map +1 -0
  91. package/dist/ledger/pricing.js +142 -0
  92. package/dist/ledger/pricing.js.map +1 -0
  93. package/dist/memory/patterns.d.ts +35 -0
  94. package/dist/memory/patterns.d.ts.map +1 -0
  95. package/dist/memory/patterns.js +152 -0
  96. package/dist/memory/patterns.js.map +1 -0
  97. package/dist/policy/decay.d.ts +95 -0
  98. package/dist/policy/decay.d.ts.map +1 -0
  99. package/dist/policy/decay.js +133 -0
  100. package/dist/policy/decay.js.map +1 -0
  101. package/dist/policy/default-rules.d.ts +21 -0
  102. package/dist/policy/default-rules.d.ts.map +1 -0
  103. package/dist/policy/default-rules.js +60 -0
  104. package/dist/policy/default-rules.js.map +1 -0
  105. package/dist/policy/gate.d.ts +116 -0
  106. package/dist/policy/gate.d.ts.map +1 -0
  107. package/dist/policy/gate.js +227 -0
  108. package/dist/policy/gate.js.map +1 -0
  109. package/dist/policy/pii.d.ts +28 -0
  110. package/dist/policy/pii.d.ts.map +1 -0
  111. package/dist/policy/pii.js +124 -0
  112. package/dist/policy/pii.js.map +1 -0
  113. package/dist/proxy.d.ts +33 -0
  114. package/dist/proxy.d.ts.map +1 -0
  115. package/dist/proxy.js +36 -0
  116. package/dist/proxy.js.map +1 -0
  117. package/dist/resilience/circuit.d.ts +87 -0
  118. package/dist/resilience/circuit.d.ts.map +1 -0
  119. package/dist/resilience/circuit.js +167 -0
  120. package/dist/resilience/circuit.js.map +1 -0
  121. package/dist/resilience/scope.d.ts +97 -0
  122. package/dist/resilience/scope.d.ts.map +1 -0
  123. package/dist/resilience/scope.js +244 -0
  124. package/dist/resilience/scope.js.map +1 -0
  125. package/dist/shared/constants.d.ts +7 -0
  126. package/dist/shared/constants.d.ts.map +1 -0
  127. package/dist/shared/constants.js +7 -0
  128. package/dist/shared/constants.js.map +1 -0
  129. package/dist/shared/errors.d.ts +31 -0
  130. package/dist/shared/errors.d.ts.map +1 -0
  131. package/dist/shared/errors.js +61 -0
  132. package/dist/shared/errors.js.map +1 -0
  133. package/dist/shared/ids.d.ts +7 -0
  134. package/dist/shared/ids.d.ts.map +1 -0
  135. package/dist/shared/ids.js +31 -0
  136. package/dist/shared/ids.js.map +1 -0
  137. package/dist/shared/types.d.ts +162 -0
  138. package/dist/shared/types.d.ts.map +1 -0
  139. package/dist/shared/types.js +41 -0
  140. package/dist/shared/types.js.map +1 -0
  141. package/dist/snapshot/checkpoint.d.ts +22 -0
  142. package/dist/snapshot/checkpoint.d.ts.map +1 -0
  143. package/dist/snapshot/checkpoint.js +172 -0
  144. package/dist/snapshot/checkpoint.js.map +1 -0
  145. package/dist/streaming.d.ts +44 -0
  146. package/dist/streaming.d.ts.map +1 -0
  147. package/dist/streaming.js +123 -0
  148. package/dist/streaming.js.map +1 -0
  149. package/package.json +54 -0
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Decay Rate Calculator
3
+ *
4
+ * Calculates exponential decay rates for quota/budget consumption.
5
+ * Enables time-weighted rate limiting that rewards idle periods.
6
+ *
7
+ * The decay model uses: value(t) = initial * e^(-lambda * t)
8
+ * where lambda is the decay constant and t is time elapsed.
9
+ *
10
+ * Adapted from Field Project fermion/src/higgs/decay-rate.ts.
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // DecayRateCalculator
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Decay rate calculator using exponential decay model.
17
+ *
18
+ * Used for time-weighted rate limiting. Older consumption contributes less
19
+ * to current budget usage, rewarding tenants who space out their requests.
20
+ */
21
+ export class DecayRateCalculator {
22
+ /** Decay constant (lambda = ln(2) / halfLife) */
23
+ lambda;
24
+ /** Minimum threshold for considering a value "fully decayed" */
25
+ minThreshold;
26
+ constructor(config) {
27
+ if (config.halfLifeMs <= 0) {
28
+ throw new Error("Half-life must be positive");
29
+ }
30
+ this.lambda = Math.LN2 / config.halfLifeMs;
31
+ this.minThreshold = config.minThreshold ?? 1e-9;
32
+ }
33
+ /**
34
+ * Calculate the decayed value after a given time elapsed.
35
+ *
36
+ * @param initialValue - The original value before decay
37
+ * @param elapsedMs - Time elapsed in milliseconds
38
+ * @returns Decay result with decayed value and metadata
39
+ */
40
+ calculate(initialValue, elapsedMs) {
41
+ if (elapsedMs < 0) {
42
+ throw new Error("Elapsed time cannot be negative");
43
+ }
44
+ if (initialValue <= 0) {
45
+ return {
46
+ decayedValue: 0,
47
+ decayFactor: 1,
48
+ elapsedMs,
49
+ fullyDecayed: true,
50
+ };
51
+ }
52
+ const decayFactor = Math.exp(-this.lambda * elapsedMs);
53
+ const decayedValue = initialValue * decayFactor;
54
+ const fullyDecayed = decayedValue < this.minThreshold;
55
+ return {
56
+ decayedValue: fullyDecayed ? 0 : decayedValue,
57
+ decayFactor,
58
+ elapsedMs,
59
+ fullyDecayed,
60
+ };
61
+ }
62
+ /**
63
+ * Calculate the weighted sum of multiple timestamped values.
64
+ * Each value is decayed based on its age relative to the reference time.
65
+ *
66
+ * @param entries - Array of timestamped entries
67
+ * @param referenceTime - The reference time (usually Date.now())
68
+ * @returns Total decayed value
69
+ */
70
+ calculateWeightedSum(entries, referenceTime = Date.now()) {
71
+ let total = 0;
72
+ for (const entry of entries) {
73
+ const elapsedMs = referenceTime - entry.ts;
74
+ if (elapsedMs < 0) {
75
+ // Future entry — use full value
76
+ total += entry.value;
77
+ }
78
+ else {
79
+ const result = this.calculate(entry.value, elapsedMs);
80
+ total += result.decayedValue;
81
+ }
82
+ }
83
+ return total;
84
+ }
85
+ /**
86
+ * Calculate time required for a value to decay to a target.
87
+ *
88
+ * @param currentValue - Current value
89
+ * @param targetValue - Target value to decay to
90
+ * @returns Time in milliseconds, or Infinity if target >= current
91
+ */
92
+ timeToDecay(currentValue, targetValue) {
93
+ if (targetValue >= currentValue || targetValue <= 0) {
94
+ return Number.POSITIVE_INFINITY;
95
+ }
96
+ // t = -ln(target/current) / lambda
97
+ return -Math.log(targetValue / currentValue) / this.lambda;
98
+ }
99
+ /**
100
+ * Get the half-life of this calculator.
101
+ */
102
+ getHalfLifeMs() {
103
+ return Math.LN2 / this.lambda;
104
+ }
105
+ }
106
+ // ---------------------------------------------------------------------------
107
+ // Convenience: calculateDecayRate function
108
+ // ---------------------------------------------------------------------------
109
+ /**
110
+ * Calculate the decay rate for a set of timestamped entries.
111
+ *
112
+ * This is the primary entry point — a pure function that creates a calculator,
113
+ * computes the weighted sum, and returns the total decayed value.
114
+ *
115
+ * @param entries - Array of timestamped entries
116
+ * @param halfLife - Half-life in milliseconds (default: 1 hour)
117
+ * @returns Total decayed value (weighted sum)
118
+ */
119
+ export function calculateDecayRate(entries, halfLife = 3_600_000) {
120
+ const calc = new DecayRateCalculator({ halfLifeMs: halfLife });
121
+ return calc.calculateWeightedSum(entries);
122
+ }
123
+ /**
124
+ * Create a decay calculator with a half-life matching a cost window.
125
+ * The half-life is set to 1/4 of the window so most decay occurs within it.
126
+ */
127
+ export function createCostDecayCalculator(windowMs) {
128
+ return new DecayRateCalculator({
129
+ halfLifeMs: windowMs / 4,
130
+ minThreshold: 0.0001, // $0.0001 threshold for cost values
131
+ });
132
+ }
133
+ //# sourceMappingURL=decay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decay.js","sourceRoot":"","sources":["../../src/policy/decay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Default Policy Rules
3
+ *
4
+ * Sensible defaults for financial governance:
5
+ * 1. Block zero-budget calls (budget <= 0 → deny, hard)
6
+ * 2. Warn on high-cost operations (estimated_cost > 1000 → warn, soft)
7
+ * 3. Block if budget exceeded (budget_remaining < estimated_cost → deny, hard)
8
+ */
9
+ import type { GateRule } from "./gate.js";
10
+ export declare const DEFAULT_RULES: GateRule[];
11
+ /**
12
+ * Custom condition check for budget-exceeded rule.
13
+ * The default rules use two `exists` conditions as a prerequisite;
14
+ * actual comparison is done via this helper since cross-field
15
+ * comparison is not expressible with single-field operators alone.
16
+ *
17
+ * Usage:
18
+ * if (isBudgetExceeded(context)) { ... }
19
+ */
20
+ export declare function isBudgetExceeded(context: Record<string, unknown>): boolean;
21
+ //# sourceMappingURL=default-rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-rules.d.ts","sourceRoot":"","sources":["../../src/policy/default-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,eAAO,MAAM,aAAa,EAAE,QAAQ,EAkCnC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAK1E"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Default Policy Rules
3
+ *
4
+ * Sensible defaults for financial governance:
5
+ * 1. Block zero-budget calls (budget <= 0 → deny, hard)
6
+ * 2. Warn on high-cost operations (estimated_cost > 1000 → warn, soft)
7
+ * 3. Block if budget exceeded (budget_remaining < estimated_cost → deny, hard)
8
+ */
9
+ export const DEFAULT_RULES = [
10
+ {
11
+ id: "block-zero-budget",
12
+ name: "Block zero-budget calls",
13
+ description: "Deny any operation when the caller has zero or negative budget",
14
+ priority: 1,
15
+ enabled: true,
16
+ effect: "deny",
17
+ enforcement: "hard",
18
+ severity: "critical",
19
+ conditions: [{ field: "budget", operator: "lte", value: 0 }],
20
+ },
21
+ {
22
+ id: "warn-high-cost",
23
+ name: "Warn on high-cost operations",
24
+ description: "Emit a warning when estimated cost exceeds 1000 tokens",
25
+ priority: 50,
26
+ enabled: true,
27
+ effect: "warn",
28
+ enforcement: "soft",
29
+ severity: "medium",
30
+ conditions: [{ field: "estimated_cost", operator: "gt", value: 1000 }],
31
+ },
32
+ {
33
+ id: "block-budget-exhausted",
34
+ name: "Block if budget exhausted",
35
+ description: "Deny operation when remaining budget is zero or negative",
36
+ priority: 2,
37
+ enabled: true,
38
+ effect: "deny",
39
+ enforcement: "hard",
40
+ severity: "high",
41
+ conditions: [{ field: "budget_remaining", operator: "lte", value: 0 }],
42
+ },
43
+ ];
44
+ /**
45
+ * Custom condition check for budget-exceeded rule.
46
+ * The default rules use two `exists` conditions as a prerequisite;
47
+ * actual comparison is done via this helper since cross-field
48
+ * comparison is not expressible with single-field operators alone.
49
+ *
50
+ * Usage:
51
+ * if (isBudgetExceeded(context)) { ... }
52
+ */
53
+ export function isBudgetExceeded(context) {
54
+ const remaining = context.budget_remaining;
55
+ const estimated = context.estimated_cost;
56
+ if (typeof remaining !== "number" || typeof estimated !== "number")
57
+ return false;
58
+ return remaining < estimated;
59
+ }
60
+ //# sourceMappingURL=default-rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-rules.js","sourceRoot":"","sources":["../../src/policy/default-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,CAAC,MAAM,aAAa,GAAe;IACxC;QACC,EAAE,EAAE,mBAAmB;QACvB,IAAI,EAAE,yBAAyB;QAC/B,WAAW,EAAE,gEAAgE;QAC7E,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,MAAM;QACd,WAAW,EAAE,MAAM;QACnB,QAAQ,EAAE,UAAU;QACpB,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KAC5D;IACD;QACC,EAAE,EAAE,gBAAgB;QACpB,IAAI,EAAE,8BAA8B;QACpC,WAAW,EAAE,wDAAwD;QACrE,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,MAAM;QACd,WAAW,EAAE,MAAM;QACnB,QAAQ,EAAE,QAAQ;QAClB,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KACtE;IACD;QACC,EAAE,EAAE,wBAAwB;QAC5B,IAAI,EAAE,2BAA2B;QACjC,WAAW,EAAE,0DAA0D;QACvE,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,MAAM;QACd,WAAW,EAAE,MAAM;QACnB,QAAQ,EAAE,MAAM;QAChB,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;KACtE;CACD,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAChE,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAC3C,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC;IACzC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACjF,OAAO,SAAS,GAAG,SAAS,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Policy Gate — 12 Field Operators
3
+ *
4
+ * Evaluates policy rules against a context to determine allow/deny decisions.
5
+ * Supports both hard (blocking) and soft (warning) enforcement modes.
6
+ *
7
+ * Adapted from Turf policy-gate.ts for the usertrust SDK.
8
+ * Uses the shared PolicyRule/FieldCondition types with dot-notation field
9
+ * resolution, glob matching on scope patterns, and time-window constraints.
10
+ *
11
+ * 12 operators: exists, not_exists, eq, neq, gt, gte, lt, lte, in, not_in,
12
+ * contains, regex.
13
+ */
14
+ import type { PolicyEffect, PolicyEnforcement, PolicyRule, PolicySeverity } from "../shared/types.js";
15
+ export interface RuleMatch {
16
+ /** Rule name */
17
+ name: string;
18
+ /** Effect of the matched rule */
19
+ effect: PolicyEffect;
20
+ /** Enforcement level */
21
+ enforcement: PolicyEnforcement;
22
+ /** Severity if set */
23
+ severity: PolicySeverity | undefined;
24
+ }
25
+ export interface PolicyResult {
26
+ /** Overall decision: deny if any hard violation, otherwise allow */
27
+ decision: "allow" | "deny";
28
+ /** Whether soft violations (warnings) were found */
29
+ hasWarnings: boolean;
30
+ /** All matched rules */
31
+ matched: RuleMatch[];
32
+ /** Hard violations that caused deny */
33
+ hardViolations: RuleMatch[];
34
+ /** Soft violations (warnings only) */
35
+ softViolations: RuleMatch[];
36
+ /** Human-readable reasons */
37
+ reasons: string[];
38
+ /** Evaluation timestamp */
39
+ evaluatedAt: string;
40
+ }
41
+ /**
42
+ * Test if any scope in the context matches any scope pattern from the rule.
43
+ * Uses minimatch for full glob support (**, *, brace expansion, etc.).
44
+ */
45
+ export declare function matchesScope(patterns: string[], scopes: string[]): boolean;
46
+ export interface TimeWindow {
47
+ /** Days of week (0=Sun, 6=Sat) */
48
+ daysOfWeek?: number[];
49
+ /** Start hour (0-23, inclusive) */
50
+ startHour?: number;
51
+ /** End hour (0-23, exclusive) */
52
+ endHour?: number;
53
+ }
54
+ /**
55
+ * Check if a timestamp falls within any of the given time windows.
56
+ * Returns true if timeWindows is empty/undefined (no constraint).
57
+ */
58
+ export declare function isWithinTimeWindow(timeWindows: TimeWindow[] | undefined, timestamp: string): boolean;
59
+ /**
60
+ * Context passed into evaluatePolicy. Fields are available for dot-notation
61
+ * resolution. The special keys `scope` and `timeWindows` enable glob and
62
+ * time-window matching respectively.
63
+ */
64
+ export interface PolicyContext extends Record<string, unknown> {
65
+ /** Optional scope patterns to match against rule scope conditions */
66
+ scope?: string[];
67
+ /** Optional time windows for temporal constraints */
68
+ timeWindows?: TimeWindow[];
69
+ /** Optional timestamp override (defaults to now) */
70
+ timestamp?: string;
71
+ }
72
+ /**
73
+ * Extended policy rule for the gate. Adds optional `id`, `description`,
74
+ * `priority`, `enabled`, `scopePatterns`, and `timeWindows` fields to the
75
+ * shared PolicyRule type.
76
+ */
77
+ export interface GateRule extends PolicyRule {
78
+ /** Unique rule identifier */
79
+ id?: string;
80
+ /** Human-readable description */
81
+ description?: string;
82
+ /** Rule priority (lower = higher priority). Default: 100 */
83
+ priority?: number;
84
+ /** Whether the rule is active. Default: true */
85
+ enabled?: boolean;
86
+ /** Glob patterns for scope matching */
87
+ scopePatterns?: string[];
88
+ /** Time windows for temporal constraints */
89
+ timeWindows?: TimeWindow[];
90
+ }
91
+ /**
92
+ * Evaluate policy rules against a context.
93
+ *
94
+ * Rules are sorted by priority (lower = higher priority, default 100).
95
+ * All matching rules are evaluated. The overall decision is "deny" if any
96
+ * hard violation is found. Soft violations produce warnings but allow.
97
+ *
98
+ * @param rules - Policy rules to evaluate
99
+ * @param context - Evaluation context with arbitrary fields
100
+ * @returns Policy evaluation result
101
+ */
102
+ export declare function evaluatePolicy(rules: GateRule[], context: PolicyContext): PolicyResult;
103
+ /**
104
+ * Load policy rules from a JSON or YAML file.
105
+ *
106
+ * Supports:
107
+ * - `.json` files: expects `{ "rules": [...] }` or a bare array
108
+ * - `.yml` / `.yaml` files: expects `rules: [...]` or a bare sequence
109
+ *
110
+ * Returns an empty array if the file cannot be read or parsed.
111
+ *
112
+ * @param path - Absolute or relative path to the policy file
113
+ * @returns Array of policy rules
114
+ */
115
+ export declare function loadPolicies(path: string): GateRule[];
116
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/policy/gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAGX,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,MAAM,oBAAoB,CAAC;AAM5B,MAAM,WAAW,SAAS;IACzB,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,MAAM,EAAE,YAAY,CAAC;IACrB,wBAAwB;IACxB,WAAW,EAAE,iBAAiB,CAAC;IAC/B,sBAAsB;IACtB,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC5B,oEAAoE;IACpE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,oDAAoD;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,wBAAwB;IACxB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,uCAAuC;IACvC,cAAc,EAAE,SAAS,EAAE,CAAC;IAC5B,sCAAsC;IACtC,cAAc,EAAE,SAAS,EAAE,CAAC;IAC5B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2BAA2B;IAC3B,WAAW,EAAE,MAAM,CAAC;CACpB;AAsFD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAE1E;AAMD,MAAM,WAAW,UAAU;IAC1B,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,WAAW,EAAE,UAAU,EAAE,GAAG,SAAS,EACrC,SAAS,EAAE,MAAM,GACf,OAAO,CAaT;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAc,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC7D,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,qDAAqD;IACrD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC3C,6BAA6B;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC3B;AAkCD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,YAAY,CAkDtF;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAiBrD"}
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Policy Gate — 12 Field Operators
3
+ *
4
+ * Evaluates policy rules against a context to determine allow/deny decisions.
5
+ * Supports both hard (blocking) and soft (warning) enforcement modes.
6
+ *
7
+ * Adapted from Turf policy-gate.ts for the usertrust SDK.
8
+ * Uses the shared PolicyRule/FieldCondition types with dot-notation field
9
+ * resolution, glob matching on scope patterns, and time-window constraints.
10
+ *
11
+ * 12 operators: exists, not_exists, eq, neq, gt, gte, lt, lte, in, not_in,
12
+ * contains, regex.
13
+ */
14
+ import { readFileSync } from "node:fs";
15
+ import { minimatch } from "minimatch";
16
+ import { parse as parseYaml } from "yaml";
17
+ // ---------------------------------------------------------------------------
18
+ // Dot-notation field resolution
19
+ // ---------------------------------------------------------------------------
20
+ /**
21
+ * Resolve a dot-separated field path from a nested object.
22
+ * E.g. "context.data.model" traverses { context: { data: { model: "x" } } }.
23
+ */
24
+ function resolveFieldPath(path, context) {
25
+ const parts = path.split(".");
26
+ let current = context;
27
+ for (const part of parts) {
28
+ if (current == null || typeof current !== "object")
29
+ return undefined;
30
+ current = current[part];
31
+ }
32
+ return current;
33
+ }
34
+ // ---------------------------------------------------------------------------
35
+ // Field condition evaluation (12 operators)
36
+ // ---------------------------------------------------------------------------
37
+ /**
38
+ * Evaluate a single field condition against the evaluation context.
39
+ * Supports all 12 operators from the FieldOperator union.
40
+ */
41
+ function evaluateFieldCondition(fc, context) {
42
+ const resolved = resolveFieldPath(fc.field, context);
43
+ switch (fc.operator) {
44
+ case "exists":
45
+ return resolved !== undefined && resolved !== null;
46
+ case "not_exists":
47
+ return resolved === undefined || resolved === null;
48
+ case "eq":
49
+ return resolved === fc.value;
50
+ case "neq":
51
+ return resolved !== fc.value;
52
+ case "gt":
53
+ return typeof resolved === "number" && typeof fc.value === "number" && resolved > fc.value;
54
+ case "gte":
55
+ return typeof resolved === "number" && typeof fc.value === "number" && resolved >= fc.value;
56
+ case "lt":
57
+ return typeof resolved === "number" && typeof fc.value === "number" && resolved < fc.value;
58
+ case "lte":
59
+ return typeof resolved === "number" && typeof fc.value === "number" && resolved <= fc.value;
60
+ case "in":
61
+ return Array.isArray(fc.value) && fc.value.includes(resolved);
62
+ case "not_in":
63
+ return Array.isArray(fc.value) && !fc.value.includes(resolved);
64
+ case "contains":
65
+ return (typeof resolved === "string" && typeof fc.value === "string" && resolved.includes(fc.value));
66
+ case "regex":
67
+ if (typeof resolved !== "string" || typeof fc.value !== "string")
68
+ return false;
69
+ try {
70
+ return new RegExp(fc.value).test(resolved);
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ default:
76
+ return false;
77
+ }
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Glob matching for scope patterns
81
+ // ---------------------------------------------------------------------------
82
+ /**
83
+ * Test if any scope in the context matches any scope pattern from the rule.
84
+ * Uses minimatch for full glob support (**, *, brace expansion, etc.).
85
+ */
86
+ export function matchesScope(patterns, scopes) {
87
+ return scopes.some((scope) => patterns.some((pattern) => minimatch(scope, pattern)));
88
+ }
89
+ /**
90
+ * Check if a timestamp falls within any of the given time windows.
91
+ * Returns true if timeWindows is empty/undefined (no constraint).
92
+ */
93
+ export function isWithinTimeWindow(timeWindows, timestamp) {
94
+ if (!timeWindows || timeWindows.length === 0)
95
+ return true;
96
+ const date = new Date(timestamp);
97
+ const dayOfWeek = date.getDay();
98
+ const hour = date.getHours();
99
+ return timeWindows.some((tw) => {
100
+ if (tw.daysOfWeek && !tw.daysOfWeek.includes(dayOfWeek))
101
+ return false;
102
+ if (tw.startHour !== undefined && hour < tw.startHour)
103
+ return false;
104
+ if (tw.endHour !== undefined && hour >= tw.endHour)
105
+ return false;
106
+ return true;
107
+ });
108
+ }
109
+ /**
110
+ * Evaluate all conditions of a rule against the context.
111
+ */
112
+ function ruleMatches(rule, context) {
113
+ const enabled = rule.enabled ?? true;
114
+ if (!enabled)
115
+ return false;
116
+ // All field conditions must match
117
+ for (const fc of rule.conditions) {
118
+ if (!evaluateFieldCondition(fc, context))
119
+ return false;
120
+ }
121
+ // Scope matching (if rule has scope patterns)
122
+ if (rule.scopePatterns && rule.scopePatterns.length > 0) {
123
+ const ctxScopes = context.scope;
124
+ if (!ctxScopes || ctxScopes.length === 0)
125
+ return false;
126
+ if (!matchesScope(rule.scopePatterns, ctxScopes))
127
+ return false;
128
+ }
129
+ // Time window matching
130
+ if (rule.timeWindows && rule.timeWindows.length > 0) {
131
+ const timestamp = context.timestamp ?? new Date().toISOString();
132
+ if (!isWithinTimeWindow(rule.timeWindows, timestamp))
133
+ return false;
134
+ }
135
+ return true;
136
+ }
137
+ // ---------------------------------------------------------------------------
138
+ // Policy evaluation (main entry point)
139
+ // ---------------------------------------------------------------------------
140
+ /**
141
+ * Evaluate policy rules against a context.
142
+ *
143
+ * Rules are sorted by priority (lower = higher priority, default 100).
144
+ * All matching rules are evaluated. The overall decision is "deny" if any
145
+ * hard violation is found. Soft violations produce warnings but allow.
146
+ *
147
+ * @param rules - Policy rules to evaluate
148
+ * @param context - Evaluation context with arbitrary fields
149
+ * @returns Policy evaluation result
150
+ */
151
+ export function evaluatePolicy(rules, context) {
152
+ const timestamp = context.timestamp ?? new Date().toISOString();
153
+ // Sort by priority (ascending — lower number = higher priority)
154
+ const sortedRules = [...rules].sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100));
155
+ const matched = [];
156
+ const hardViolations = [];
157
+ const softViolations = [];
158
+ const reasons = [];
159
+ for (const rule of sortedRules) {
160
+ if (!ruleMatches(rule, context))
161
+ continue;
162
+ const match = {
163
+ name: rule.name,
164
+ effect: rule.effect,
165
+ enforcement: rule.enforcement,
166
+ severity: rule.severity,
167
+ };
168
+ matched.push(match);
169
+ // Classify violation
170
+ const isViolation = rule.effect === "deny" || rule.effect === "warn";
171
+ if (isViolation) {
172
+ const label = rule.id ? `[${rule.id}]` : `[${rule.name}]`;
173
+ const rationale = rule.description ?? rule.name;
174
+ const reason = rule.enforcement === "hard" ? `${label} ${rationale}` : `[WARN] ${label} ${rationale}`;
175
+ reasons.push(reason);
176
+ if (rule.enforcement === "hard") {
177
+ hardViolations.push(match);
178
+ }
179
+ else {
180
+ softViolations.push(match);
181
+ }
182
+ }
183
+ }
184
+ return {
185
+ decision: hardViolations.length > 0 ? "deny" : "allow",
186
+ hasWarnings: softViolations.length > 0,
187
+ matched,
188
+ hardViolations,
189
+ softViolations,
190
+ reasons,
191
+ evaluatedAt: timestamp,
192
+ };
193
+ }
194
+ // ---------------------------------------------------------------------------
195
+ // Policy file loading
196
+ // ---------------------------------------------------------------------------
197
+ /**
198
+ * Load policy rules from a JSON or YAML file.
199
+ *
200
+ * Supports:
201
+ * - `.json` files: expects `{ "rules": [...] }` or a bare array
202
+ * - `.yml` / `.yaml` files: expects `rules: [...]` or a bare sequence
203
+ *
204
+ * Returns an empty array if the file cannot be read or parsed.
205
+ *
206
+ * @param path - Absolute or relative path to the policy file
207
+ * @returns Array of policy rules
208
+ */
209
+ export function loadPolicies(path) {
210
+ try {
211
+ const raw = readFileSync(path, "utf-8");
212
+ const isYaml = path.endsWith(".yml") || path.endsWith(".yaml");
213
+ const parsed = isYaml ? parseYaml(raw) : JSON.parse(raw);
214
+ if (Array.isArray(parsed))
215
+ return parsed;
216
+ if (parsed !== null && typeof parsed === "object") {
217
+ const obj = parsed;
218
+ if (Array.isArray(obj.rules))
219
+ return obj.rules;
220
+ }
221
+ return [];
222
+ }
223
+ catch {
224
+ return [];
225
+ }
226
+ }
227
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/policy/gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * PII Detector
3
+ *
4
+ * Pattern-based PII detection for event payloads.
5
+ * Recursively scans strings in objects/arrays and returns
6
+ * which PII types were found and at which paths.
7
+ *
8
+ * Pure module — no side effects, no network calls.
9
+ *
10
+ * Detects: email, phone, SSN, credit card (Luhn-validated), IPv4.
11
+ *
12
+ * Adapted from Field Project fermion/src/higgs/pii-detector.ts.
13
+ */
14
+ export interface PIIDetection {
15
+ /** Whether any PII was found */
16
+ found: boolean;
17
+ /** PII types detected (e.g. ["email", "ssn"]) */
18
+ types: string[];
19
+ /** Dot-paths where PII was found (e.g. ["user.email(email)", "billing.ssn(ssn)"]) */
20
+ paths: string[];
21
+ }
22
+ /**
23
+ * Detect PII in any value by recursively scanning strings in objects/arrays.
24
+ *
25
+ * Returns which PII types were found and at which dot-paths.
26
+ */
27
+ export declare function detectPII(data: unknown): PIIDetection;
28
+ //# sourceMappingURL=pii.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pii.d.ts","sourceRoot":"","sources":["../../src/policy/pii.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,MAAM,WAAW,YAAY;IAC5B,gCAAgC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,qFAAqF;IACrF,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB;AAiFD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAWrD"}