agent-passport-system 1.28.0 → 1.29.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 +4 -4
- package/dist/src/core/anchor-state.d.ts +46 -0
- package/dist/src/core/anchor-state.d.ts.map +1 -0
- package/dist/src/core/anchor-state.js +80 -0
- package/dist/src/core/anchor-state.js.map +1 -0
- package/dist/src/core/canonical-jcs.d.ts +23 -0
- package/dist/src/core/canonical-jcs.d.ts.map +1 -0
- package/dist/src/core/canonical-jcs.js +125 -0
- package/dist/src/core/canonical-jcs.js.map +1 -0
- package/dist/src/core/data-narrowing.d.ts +43 -0
- package/dist/src/core/data-narrowing.d.ts.map +1 -0
- package/dist/src/core/data-narrowing.js +97 -0
- package/dist/src/core/data-narrowing.js.map +1 -0
- package/dist/src/core/denial-domains.d.ts +43 -0
- package/dist/src/core/denial-domains.d.ts.map +1 -0
- package/dist/src/core/denial-domains.js +153 -0
- package/dist/src/core/denial-domains.js.map +1 -0
- package/dist/src/core/fidelity-probe.d.ts +45 -0
- package/dist/src/core/fidelity-probe.d.ts.map +1 -1
- package/dist/src/core/fidelity-probe.js +39 -1
- package/dist/src/core/fidelity-probe.js.map +1 -1
- package/dist/src/core/governance-posture.d.ts +72 -0
- package/dist/src/core/governance-posture.d.ts.map +1 -0
- package/dist/src/core/governance-posture.js +173 -0
- package/dist/src/core/governance-posture.js.map +1 -0
- package/dist/src/core/reputation-authority.d.ts +13 -4
- package/dist/src/core/reputation-authority.d.ts.map +1 -1
- package/dist/src/core/reputation-authority.js +29 -5
- package/dist/src/core/reputation-authority.js.map +1 -1
- package/dist/src/index.d.ts +11 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/reputation-authority.d.ts +4 -0
- package/dist/src/types/reputation-authority.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/agent-passport-system)
|
|
4
4
|
[](https://github.com/aeoess/agent-passport-system/blob/main/LICENSE)
|
|
5
|
-
[](https://github.com/aeoess/agent-passport-system)
|
|
6
6
|
[](https://doi.org/10.5281/zenodo.18749779)
|
|
7
7
|
|
|
8
8
|
> **For AI agents:** visit [aeoess.com/llms.txt](https://aeoess.com/llms.txt) for machine-readable docs or [llms-full.txt](https://aeoess.com/llms-full.txt) for the complete reference.
|
|
@@ -124,7 +124,7 @@ const agent = joinSocialContract({ name: 'my-agent', owner: 'alice', floor: floo
|
|
|
124
124
|
|
|
125
125
|
## The Stack
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
62 core modules + 32 v2 constitutional modules. 1715 tests. Zero heavy dependencies.
|
|
128
128
|
|
|
129
129
|
| Layer | What it does | Key primitive |
|
|
130
130
|
|-------|-------------|---------------|
|
|
@@ -178,7 +178,7 @@ npx agent-passport audit --floor values/floor.yaml
|
|
|
178
178
|
|
|
179
179
|
```bash
|
|
180
180
|
npm test
|
|
181
|
-
#
|
|
181
|
+
# 1715 tests across 93 files, 446 suites, 0 failures
|
|
182
182
|
```
|
|
183
183
|
|
|
184
184
|
50 adversarial tests: Merkle tampering, attribution gaming, compliance violations, floor negotiation attacks, cross-chain confused deputy, taint laundering, authority probing.
|
|
@@ -196,7 +196,7 @@ npm test
|
|
|
196
196
|
| Signed receipts | 3-sig chain | Proposed | Logs | General | — |
|
|
197
197
|
| Values enforcement | 8 principles, graduated | — | Rules | — | — |
|
|
198
198
|
| Coordination | Task lifecycle + MCP | — | — | — | — |
|
|
199
|
-
| Tests |
|
|
199
|
+
| Tests | 1715 (50 adversarial) | None | Limited | None | None |
|
|
200
200
|
|
|
201
201
|
## Recognition
|
|
202
202
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** External anchor state for a receipt or batch.
|
|
2
|
+
* unanchored: exists only in gateway memory
|
|
3
|
+
* batched_pending: included in a Merkle batch, root not yet anchored externally
|
|
4
|
+
* anchored: Merkle root published to external log (Rekor, Solana, etc.)
|
|
5
|
+
* critical_direct_anchor: individual receipt anchored directly (bypass batching) */
|
|
6
|
+
export type AnchorState = 'unanchored' | 'batched_pending' | 'anchored' | 'critical_direct_anchor';
|
|
7
|
+
/** Anchor metadata on a receipt */
|
|
8
|
+
export interface AnchorMetadata {
|
|
9
|
+
state: AnchorState;
|
|
10
|
+
/** Batch ID if batched_pending or anchored */
|
|
11
|
+
batchId?: string;
|
|
12
|
+
/** External anchor reference (URL, transaction ID, etc.) */
|
|
13
|
+
anchorRef?: string;
|
|
14
|
+
/** When the anchor was confirmed */
|
|
15
|
+
anchoredAt?: string;
|
|
16
|
+
/** Which anchor backend was used */
|
|
17
|
+
anchorBackend?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Auto-batch configuration */
|
|
20
|
+
export interface AutoBatchConfig {
|
|
21
|
+
/** Maximum seconds between batch commits (0 = disabled) */
|
|
22
|
+
maxIntervalSeconds: number;
|
|
23
|
+
/** Maximum receipts before auto-commit (0 = disabled) */
|
|
24
|
+
maxReceiptsPerBatch: number;
|
|
25
|
+
/** Whether critical/irreversible actions get direct anchor */
|
|
26
|
+
directAnchorCritical: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare const DEFAULT_AUTO_BATCH_CONFIG: AutoBatchConfig;
|
|
29
|
+
/** Create initial anchor metadata for a new receipt */
|
|
30
|
+
export declare function createAnchorMetadata(critical?: boolean): AnchorMetadata;
|
|
31
|
+
/** Transition anchor state when receipt is added to a batch */
|
|
32
|
+
export declare function markBatched(anchor: AnchorMetadata, batchId: string): AnchorMetadata;
|
|
33
|
+
/** Transition anchor state when batch root is externally anchored */
|
|
34
|
+
export declare function markAnchored(anchor: AnchorMetadata, anchorRef: string, anchorBackend: string): AnchorMetadata;
|
|
35
|
+
/** Check if auto-batch should fire based on config and current state */
|
|
36
|
+
export declare function shouldAutoBatch(pendingCount: number, lastBatchTime: string | null, config?: AutoBatchConfig): {
|
|
37
|
+
trigger: boolean;
|
|
38
|
+
reason: 'max_receipts' | 'max_interval' | null;
|
|
39
|
+
};
|
|
40
|
+
/** Check if an anchor state meets a minimum requirement */
|
|
41
|
+
export declare function meetsAnchorRequirement(current: AnchorState, minimum: AnchorState): boolean;
|
|
42
|
+
/** Check if anchor state transition is valid (can only move forward) */
|
|
43
|
+
export declare function isValidAnchorTransition(from: AnchorState, to: AnchorState): boolean;
|
|
44
|
+
/** Exported ordering for cross-language verification */
|
|
45
|
+
export declare const ANCHOR_STATE_ORDER: Record<AnchorState, number>;
|
|
46
|
+
//# sourceMappingURL=anchor-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-state.d.ts","sourceRoot":"","sources":["../../../src/core/anchor-state.ts"],"names":[],"mappings":"AAYA;;;;qFAIqF;AACrF,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,iBAAiB,GAAG,UAAU,GAAG,wBAAwB,CAAA;AAElG,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,CAAA;IAClB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,kBAAkB,EAAE,MAAM,CAAA;IAC1B,yDAAyD;IACzD,mBAAmB,EAAE,MAAM,CAAA;IAC3B,8DAA8D;IAC9D,oBAAoB,EAAE,OAAO,CAAA;CAC9B;AAED,eAAO,MAAM,yBAAyB,EAAE,eAIvC,CAAA;AAED,uDAAuD;AACvD,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,OAAe,GAAG,cAAc,CAI9E;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAInF;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,cAAc,CAQhB;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,MAAM,GAAE,eAA2C,GAClD;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,GAAG,cAAc,GAAG,IAAI,CAAA;CAAE,CAsBtE;AAUD,2DAA2D;AAC3D,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,WAAW,GACnB,OAAO,CAET;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,GAAG,OAAO,CAEnF;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,6BAAe,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ══════════════════════════════════════════════════════════════════
|
|
2
|
+
// Anchor States — External verifiability tracking for receipts
|
|
3
|
+
// ══════════════════════════════════════════════════════════════════
|
|
4
|
+
// Consilium Priority 6. Gemini: "explicit receipt anchor states."
|
|
5
|
+
// desiorac (A2A #1672): batch commitment lags individual receipts.
|
|
6
|
+
//
|
|
7
|
+
// Every receipt and batch carries an anchor state:
|
|
8
|
+
// unanchored → batched_pending → anchored → critical_direct_anchor
|
|
9
|
+
//
|
|
10
|
+
// Auto-batching: configurable window (N seconds or N receipts).
|
|
11
|
+
// ══════════════════════════════════════════════════════════════════
|
|
12
|
+
export const DEFAULT_AUTO_BATCH_CONFIG = {
|
|
13
|
+
maxIntervalSeconds: 300, // 5 minutes
|
|
14
|
+
maxReceiptsPerBatch: 100,
|
|
15
|
+
directAnchorCritical: true,
|
|
16
|
+
};
|
|
17
|
+
/** Create initial anchor metadata for a new receipt */
|
|
18
|
+
export function createAnchorMetadata(critical = false) {
|
|
19
|
+
return {
|
|
20
|
+
state: critical ? 'critical_direct_anchor' : 'unanchored',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Transition anchor state when receipt is added to a batch */
|
|
24
|
+
export function markBatched(anchor, batchId) {
|
|
25
|
+
if (anchor.state === 'critical_direct_anchor')
|
|
26
|
+
return anchor; // already anchored
|
|
27
|
+
if (anchor.state === 'anchored')
|
|
28
|
+
return anchor; // already anchored
|
|
29
|
+
return { ...anchor, state: 'batched_pending', batchId };
|
|
30
|
+
}
|
|
31
|
+
/** Transition anchor state when batch root is externally anchored */
|
|
32
|
+
export function markAnchored(anchor, anchorRef, anchorBackend) {
|
|
33
|
+
if (anchor.state === 'critical_direct_anchor')
|
|
34
|
+
return anchor;
|
|
35
|
+
return {
|
|
36
|
+
...anchor,
|
|
37
|
+
state: 'anchored',
|
|
38
|
+
anchorRef, anchorBackend,
|
|
39
|
+
anchoredAt: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Check if auto-batch should fire based on config and current state */
|
|
43
|
+
export function shouldAutoBatch(pendingCount, lastBatchTime, config = DEFAULT_AUTO_BATCH_CONFIG) {
|
|
44
|
+
if (pendingCount === 0)
|
|
45
|
+
return { trigger: false, reason: null };
|
|
46
|
+
// Receipt count trigger
|
|
47
|
+
if (config.maxReceiptsPerBatch > 0 && pendingCount >= config.maxReceiptsPerBatch) {
|
|
48
|
+
return { trigger: true, reason: 'max_receipts' };
|
|
49
|
+
}
|
|
50
|
+
// Time interval trigger
|
|
51
|
+
if (config.maxIntervalSeconds > 0 && lastBatchTime) {
|
|
52
|
+
const elapsed = (Date.now() - new Date(lastBatchTime).getTime()) / 1000;
|
|
53
|
+
if (elapsed >= config.maxIntervalSeconds) {
|
|
54
|
+
return { trigger: true, reason: 'max_interval' };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// First batch ever — trigger on interval if no previous batch
|
|
58
|
+
if (config.maxIntervalSeconds > 0 && !lastBatchTime && pendingCount > 0) {
|
|
59
|
+
return { trigger: true, reason: 'max_interval' };
|
|
60
|
+
}
|
|
61
|
+
return { trigger: false, reason: null };
|
|
62
|
+
}
|
|
63
|
+
/** Anchor state ordering — higher number = more externally verifiable */
|
|
64
|
+
const ANCHOR_ORDER = {
|
|
65
|
+
unanchored: 0,
|
|
66
|
+
batched_pending: 1,
|
|
67
|
+
anchored: 2,
|
|
68
|
+
critical_direct_anchor: 3,
|
|
69
|
+
};
|
|
70
|
+
/** Check if an anchor state meets a minimum requirement */
|
|
71
|
+
export function meetsAnchorRequirement(current, minimum) {
|
|
72
|
+
return ANCHOR_ORDER[current] >= ANCHOR_ORDER[minimum];
|
|
73
|
+
}
|
|
74
|
+
/** Check if anchor state transition is valid (can only move forward) */
|
|
75
|
+
export function isValidAnchorTransition(from, to) {
|
|
76
|
+
return ANCHOR_ORDER[to] >= ANCHOR_ORDER[from];
|
|
77
|
+
}
|
|
78
|
+
/** Exported ordering for cross-language verification */
|
|
79
|
+
export const ANCHOR_STATE_ORDER = ANCHOR_ORDER;
|
|
80
|
+
//# sourceMappingURL=anchor-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-state.js","sourceRoot":"","sources":["../../../src/core/anchor-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** RFC 8785 JSON Canonicalization Scheme.
|
|
2
|
+
* Differences from legacy canonicalize():
|
|
3
|
+
* - null values ARE preserved (not filtered)
|
|
4
|
+
* - undefined object values become null
|
|
5
|
+
* - Number serialization follows ES2015 spec
|
|
6
|
+
* - All other behavior is identical (sorted keys, no whitespace) */
|
|
7
|
+
export declare function canonicalizeJCS(value: unknown): string;
|
|
8
|
+
/** Detect which canonicalization variant was likely used.
|
|
9
|
+
* Checks if null values are present — JCS preserves them, legacy strips them. */
|
|
10
|
+
export declare function detectCanonicalVariant(obj: unknown, canonicalString: string): 'jcs' | 'legacy' | 'ambiguous';
|
|
11
|
+
/** Cross-language test vector for canonicalization verification */
|
|
12
|
+
export interface CanonicalizationTestVector {
|
|
13
|
+
id: string;
|
|
14
|
+
description: string;
|
|
15
|
+
input: unknown;
|
|
16
|
+
expected_jcs: string;
|
|
17
|
+
expected_legacy: string;
|
|
18
|
+
sha256_jcs: string;
|
|
19
|
+
sha256_legacy: string;
|
|
20
|
+
}
|
|
21
|
+
/** Built-in test vectors for cross-language verification */
|
|
22
|
+
export declare function getTestVectors(): CanonicalizationTestVector[];
|
|
23
|
+
//# sourceMappingURL=canonical-jcs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-jcs.d.ts","sourceRoot":"","sources":["../../../src/core/canonical-jcs.ts"],"names":[],"mappings":"AAcA;;;;;qEAKqE;AACrE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiCtD;AAED;kFACkF;AAClF,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,MAAM,GACtB,KAAK,GAAG,QAAQ,GAAG,WAAW,CAMhC;AAYD,mEAAmE;AACnE,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACtB;AAOD,4DAA4D;AAC5D,wBAAgB,cAAc,IAAI,0BAA0B,EAAE,CAoF7D"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// ══════════════════════════════════════════════════════════════════
|
|
2
|
+
// JCS Canonicalization — RFC 8785 compliant JSON Canonicalization
|
|
3
|
+
// ══════════════════════════════════════════════════════════════════
|
|
4
|
+
// The original canonicalize() filters null values — a deviation from
|
|
5
|
+
// RFC 8785 that cannot be changed without breaking existing signatures.
|
|
6
|
+
//
|
|
7
|
+
// This module provides:
|
|
8
|
+
// canonicalizeJCS() — strict RFC 8785 compliance
|
|
9
|
+
// verifyCanonical() — detect which variant was used
|
|
10
|
+
//
|
|
11
|
+
// Migration: new signatures should use JCS. Old signatures keep
|
|
12
|
+
// working with the legacy function. Verification tries both.
|
|
13
|
+
// ══════════════════════════════════════════════════════════════════
|
|
14
|
+
/** RFC 8785 JSON Canonicalization Scheme.
|
|
15
|
+
* Differences from legacy canonicalize():
|
|
16
|
+
* - null values ARE preserved (not filtered)
|
|
17
|
+
* - undefined object values become null
|
|
18
|
+
* - Number serialization follows ES2015 spec
|
|
19
|
+
* - All other behavior is identical (sorted keys, no whitespace) */
|
|
20
|
+
export function canonicalizeJCS(value) {
|
|
21
|
+
if (value === null || value === undefined)
|
|
22
|
+
return 'null';
|
|
23
|
+
switch (typeof value) {
|
|
24
|
+
case 'boolean':
|
|
25
|
+
return value ? 'true' : 'false';
|
|
26
|
+
case 'number': {
|
|
27
|
+
if (!isFinite(value))
|
|
28
|
+
throw new Error('JCS does not support Infinity or NaN');
|
|
29
|
+
// ES2015 number serialization — JSON.stringify handles this correctly
|
|
30
|
+
return JSON.stringify(value);
|
|
31
|
+
}
|
|
32
|
+
case 'string':
|
|
33
|
+
return JSON.stringify(value);
|
|
34
|
+
case 'object': {
|
|
35
|
+
if (value instanceof Date)
|
|
36
|
+
return JSON.stringify(value);
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
return '[' + value.map(item => canonicalizeJCS(item)).join(',') + ']';
|
|
39
|
+
}
|
|
40
|
+
// Object: sort keys by Unicode code point, preserve null values
|
|
41
|
+
const obj = value;
|
|
42
|
+
const keys = Object.keys(obj).sort();
|
|
43
|
+
const pairs = [];
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
const v = obj[key];
|
|
46
|
+
// RFC 8785: undefined becomes null, null is preserved
|
|
47
|
+
// Only skip if the key was never set (shouldn't happen with Object.keys)
|
|
48
|
+
pairs.push(`${JSON.stringify(key)}:${canonicalizeJCS(v)}`);
|
|
49
|
+
}
|
|
50
|
+
return '{' + pairs.join(',') + '}';
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
throw new Error(`JCS: unsupported type ${typeof value}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Detect which canonicalization variant was likely used.
|
|
57
|
+
* Checks if null values are present — JCS preserves them, legacy strips them. */
|
|
58
|
+
export function detectCanonicalVariant(obj, canonicalString) {
|
|
59
|
+
// If the object has no null values, both variants produce identical output
|
|
60
|
+
if (!hasNullValues(obj))
|
|
61
|
+
return 'ambiguous';
|
|
62
|
+
// If canonical string contains `:null`, it's JCS (legacy strips nulls)
|
|
63
|
+
if (canonicalString.includes(':null'))
|
|
64
|
+
return 'jcs';
|
|
65
|
+
return 'legacy';
|
|
66
|
+
}
|
|
67
|
+
function hasNullValues(obj) {
|
|
68
|
+
if (obj === null)
|
|
69
|
+
return true;
|
|
70
|
+
if (typeof obj !== 'object' || obj === undefined)
|
|
71
|
+
return false;
|
|
72
|
+
if (Array.isArray(obj))
|
|
73
|
+
return obj.some(hasNullValues);
|
|
74
|
+
return Object.values(obj).some(v => v === null || v === undefined || hasNullValues(v));
|
|
75
|
+
}
|
|
76
|
+
import { createHash } from 'crypto';
|
|
77
|
+
/** Generate SHA-256 hex digest of a string */
|
|
78
|
+
function sha256hex(input) {
|
|
79
|
+
return createHash('sha256').update(input, 'utf-8').digest('hex');
|
|
80
|
+
}
|
|
81
|
+
/** Built-in test vectors for cross-language verification */
|
|
82
|
+
export function getTestVectors() {
|
|
83
|
+
const vectors = [];
|
|
84
|
+
function addVector(id, desc, input, jcs, legacy) {
|
|
85
|
+
vectors.push({
|
|
86
|
+
id, description: desc, input,
|
|
87
|
+
expected_jcs: jcs, expected_legacy: legacy,
|
|
88
|
+
sha256_jcs: sha256hex(jcs), sha256_legacy: sha256hex(legacy),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// V1: Simple object — both variants identical
|
|
92
|
+
addVector('cv-001', 'Simple object, no nulls — variants identical', { agentId: 'agent-001', scope: 'read' }, '{"agentId":"agent-001","scope":"read"}', '{"agentId":"agent-001","scope":"read"}');
|
|
93
|
+
// V2: Object with null — variants diverge
|
|
94
|
+
addVector('cv-002', 'Null value — JCS preserves, legacy strips', { agentId: 'agent-001', metadata: null, scope: 'read' }, '{"agentId":"agent-001","metadata":null,"scope":"read"}', '{"agentId":"agent-001","scope":"read"}');
|
|
95
|
+
// V3: Key ordering
|
|
96
|
+
addVector('cv-003', 'Keys sorted by Unicode code point', { zebra: 1, alpha: 2, middle: 3 }, '{"alpha":2,"middle":3,"zebra":1}', '{"alpha":2,"middle":3,"zebra":1}');
|
|
97
|
+
// V4: Nested objects with null
|
|
98
|
+
addVector('cv-004', 'Nested object with null at depth', { outer: { inner: null, value: 42 }, top: 'ok' }, '{"outer":{"inner":null,"value":42},"top":"ok"}', '{"outer":{"value":42},"top":"ok"}');
|
|
99
|
+
// V5: Arrays with null elements
|
|
100
|
+
addVector('cv-005', 'Array with null elements — both preserve array nulls', { items: [1, null, 3] }, '{"items":[1,null,3]}', '{"items":[1,null,3]}');
|
|
101
|
+
// V6: Number edge cases
|
|
102
|
+
addVector('cv-006', 'Number formatting — integers and floats', { integer: 42, negative: -7, float: 3.14, zero: 0 }, '{"float":3.14,"integer":42,"negative":-7,"zero":0}', '{"float":3.14,"integer":42,"negative":-7,"zero":0}');
|
|
103
|
+
// V7: Empty structures
|
|
104
|
+
addVector('cv-007', 'Empty object and empty array', { emptyArr: [], emptyObj: {} }, '{"emptyArr":[],"emptyObj":{}}', '{"emptyArr":[],"emptyObj":{}}');
|
|
105
|
+
// V8: Unicode
|
|
106
|
+
addVector('cv-008', 'Unicode string content', { name: 'Тимофій', emoji: '🔐' }, '{"emoji":"🔐","name":"Тимофій"}', '{"emoji":"🔐","name":"Тимофій"}');
|
|
107
|
+
// V9: Realistic APS object — delegation-like structure
|
|
108
|
+
addVector('cv-009', 'Realistic delegation object with mixed null/present fields', {
|
|
109
|
+
delegationId: 'del_abc123',
|
|
110
|
+
delegatedBy: 'did:aps:principal001',
|
|
111
|
+
delegatedTo: 'did:aps:agent002',
|
|
112
|
+
scope: ['data:read', 'commerce:checkout'],
|
|
113
|
+
spendLimit: 500,
|
|
114
|
+
obligationBundleHash: null,
|
|
115
|
+
expiresAt: '2026-04-01T00:00:00Z',
|
|
116
|
+
notBefore: null,
|
|
117
|
+
maxDepth: 3,
|
|
118
|
+
currentDepth: 1,
|
|
119
|
+
createdAt: '2026-03-29T00:00:00Z',
|
|
120
|
+
}, '{"createdAt":"2026-03-29T00:00:00Z","currentDepth":1,"delegatedBy":"did:aps:principal001","delegatedTo":"did:aps:agent002","delegationId":"del_abc123","expiresAt":"2026-04-01T00:00:00Z","maxDepth":3,"notBefore":null,"obligationBundleHash":null,"scope":["data:read","commerce:checkout"],"spendLimit":500}', '{"createdAt":"2026-03-29T00:00:00Z","currentDepth":1,"delegatedBy":"did:aps:principal001","delegatedTo":"did:aps:agent002","delegationId":"del_abc123","expiresAt":"2026-04-01T00:00:00Z","maxDepth":3,"scope":["data:read","commerce:checkout"],"spendLimit":500}');
|
|
121
|
+
// V10: Boolean values
|
|
122
|
+
addVector('cv-010', 'Boolean values', { active: true, revoked: false }, '{"active":true,"revoked":false}', '{"active":true,"revoked":false}');
|
|
123
|
+
return vectors;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=canonical-jcs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-jcs.js","sourceRoot":"","sources":["../../../src/core/canonical-jcs.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,kEAAkE;AAClE,qEAAqE;AACrE,qEAAqE;AACrE,wEAAwE;AACxE,EAAE;AACF,wBAAwB;AACxB,mDAAmD;AACnD,uDAAuD;AACvD,EAAE;AACF,gEAAgE;AAChE,6DAA6D;AAC7D,qEAAqE;AAErE;;;;;qEAKqE;AACrE,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IAExD,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QACjC,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;YAC7E,sEAAsE;YACtE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAC9B,CAAC;QACD,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAC9B,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,KAAK,YAAY,IAAI;gBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YACvD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;YACvE,CAAC;YACD,gEAAgE;YAChE,MAAM,GAAG,GAAG,KAAgC,CAAA;YAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;YACpC,MAAM,KAAK,GAAa,EAAE,CAAA;YAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;gBAClB,sDAAsD;gBACtD,yEAAyE;gBACzE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAC5D,CAAC;YACD,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;QACpC,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,KAAK,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC;AAED;kFACkF;AAClF,MAAM,UAAU,sBAAsB,CACpC,GAAY,EACZ,eAAuB;IAEvB,2EAA2E;IAC3E,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,WAAW,CAAA;IAC3C,uEAAuE;IACvE,IAAI,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACnD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,aAAa,CAAC,GAAY;IACjC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,GAA8B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC5D,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;AACtD,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAanC,8CAA8C;AAC9C,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAClE,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAiC,EAAE,CAAA;IAEhD,SAAS,SAAS,CAAC,EAAU,EAAE,IAAY,EAAE,KAAc,EAAE,GAAW,EAAE,MAAc;QACtF,OAAO,CAAC,IAAI,CAAC;YACX,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK;YAC5B,YAAY,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM;YAC1C,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC;SAC7D,CAAC,CAAA;IACJ,CAAC;IAED,8CAA8C;IAC9C,SAAS,CAAC,QAAQ,EAAE,8CAA8C,EAChE,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EACvC,wCAAwC,EACxC,wCAAwC,CAAC,CAAA;IAE3C,0CAA0C;IAC1C,SAAS,CAAC,QAAQ,EAAE,2CAA2C,EAC7D,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EACvD,wDAAwD,EACxD,wCAAwC,CAAC,CAAA;IAE3C,mBAAmB;IACnB,SAAS,CAAC,QAAQ,EAAE,mCAAmC,EACrD,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EACjC,kCAAkC,EAClC,kCAAkC,CAAC,CAAA;IAErC,+BAA+B;IAC/B,SAAS,CAAC,QAAQ,EAAE,kCAAkC,EACpD,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAChD,gDAAgD,EAChD,mCAAmC,CAAC,CAAA;IAEtC,gCAAgC;IAChC,SAAS,CAAC,QAAQ,EAAE,sDAAsD,EACxE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EACvB,sBAAsB,EACtB,sBAAsB,CAAC,CAAA;IAEzB,wBAAwB;IACxB,SAAS,CAAC,QAAQ,EAAE,yCAAyC,EAC3D,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EACnD,oDAAoD,EACpD,oDAAoD,CAAC,CAAA;IAEvD,uBAAuB;IACvB,SAAS,CAAC,QAAQ,EAAE,8BAA8B,EAChD,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAC9B,+BAA+B,EAC/B,+BAA+B,CAAC,CAAA;IAElC,cAAc;IACd,SAAS,CAAC,QAAQ,EAAE,wBAAwB,EAC1C,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,EAChC,iCAAiC,EACjC,iCAAiC,CAAC,CAAA;IAEpC,uDAAuD;IACvD,SAAS,CAAC,QAAQ,EAAE,4DAA4D,EAC9E;QACE,YAAY,EAAE,YAAY;QAC1B,WAAW,EAAE,sBAAsB;QACnC,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,CAAC,WAAW,EAAE,mBAAmB,CAAC;QACzC,UAAU,EAAE,GAAG;QACf,oBAAoB,EAAE,IAAI;QAC1B,SAAS,EAAE,sBAAsB;QACjC,SAAS,EAAE,IAAI;QACf,QAAQ,EAAE,CAAC;QACX,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,sBAAsB;KAClC,EACD,iTAAiT,EACjT,oQAAoQ,CAAC,CAAA;IAEvQ,sBAAsB;IACtB,SAAS,CAAC,QAAQ,EAAE,gBAAgB,EAClC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAChC,iCAAiC,EACjC,iCAAiC,CAAC,CAAA;IAEpC,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ConstraintFacet, ConstraintStatus } from '../types/gateway.js';
|
|
2
|
+
/** A facet evaluation snapshot — facet name + its status */
|
|
3
|
+
export interface FacetSnapshot {
|
|
4
|
+
facet: ConstraintFacet;
|
|
5
|
+
status: ConstraintStatus;
|
|
6
|
+
}
|
|
7
|
+
/** Result of a narrowing check */
|
|
8
|
+
export interface NarrowingCheckResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
/** Facets where data attempted to widen authority */
|
|
11
|
+
violations: Array<{
|
|
12
|
+
facet: ConstraintFacet;
|
|
13
|
+
before: ConstraintStatus;
|
|
14
|
+
after: ConstraintStatus;
|
|
15
|
+
message: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/** Assert that data influence only narrows authority.
|
|
19
|
+
* Compares constraint evaluations BEFORE and AFTER data is considered.
|
|
20
|
+
* Any facet that moves from a more restrictive status to a more
|
|
21
|
+
* permissive one is a violation — data attempted to widen authority.
|
|
22
|
+
*
|
|
23
|
+
* @param before - Facet evaluations before data influence
|
|
24
|
+
* @param after - Facet evaluations after data influence
|
|
25
|
+
* @returns NarrowingCheckResult with any violations */
|
|
26
|
+
export declare function assertDataNarrowsOnly(before: FacetSnapshot[], after: FacetSnapshot[]): NarrowingCheckResult;
|
|
27
|
+
/** Apply data-sourced constraint modifications safely.
|
|
28
|
+
* Only allows narrowing (making more restrictive).
|
|
29
|
+
* Returns the narrowed snapshots with any widening attempts rejected.
|
|
30
|
+
*
|
|
31
|
+
* Use case: a data source declares "this data requires scope:read_only"
|
|
32
|
+
* → the constraint is narrowed. But if data declares "grant scope:admin"
|
|
33
|
+
* → the widening is rejected and the original constraint stands. */
|
|
34
|
+
export declare function applyDataConstraints(current: FacetSnapshot[], dataInfluence: FacetSnapshot[]): {
|
|
35
|
+
result: FacetSnapshot[];
|
|
36
|
+
rejected: NarrowingCheckResult['violations'];
|
|
37
|
+
};
|
|
38
|
+
/** Check if a status transition is valid narrowing (same or more restrictive) */
|
|
39
|
+
export declare function isValidNarrowing(before: ConstraintStatus, after: ConstraintStatus): boolean;
|
|
40
|
+
/** The status ordering: fail < unknown < not_applicable < pass.
|
|
41
|
+
* Exported for test vectors and cross-language verification. */
|
|
42
|
+
export declare const NARROWING_ORDER: Record<ConstraintStatus, number>;
|
|
43
|
+
//# sourceMappingURL=data-narrowing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-narrowing.d.ts","sourceRoot":"","sources":["../../../src/core/data-narrowing.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAE5E,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,eAAe,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;CACzB;AAED,kCAAkC;AAClC,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAA;IACd,qDAAqD;IACrD,UAAU,EAAE,KAAK,CAAC;QAChB,KAAK,EAAE,eAAe,CAAA;QACtB,MAAM,EAAE,gBAAgB,CAAA;QACxB,KAAK,EAAE,gBAAgB,CAAA;QACvB,OAAO,EAAE,MAAM,CAAA;KAChB,CAAC,CAAA;CACH;AAWD;;;;;;;wDAOwD;AACxD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EAAE,EACvB,KAAK,EAAE,aAAa,EAAE,GACrB,oBAAoB,CAwBtB;AAED;;;;;;qEAMqE;AACrE,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EAAE,EACxB,aAAa,EAAE,aAAa,EAAE,GAC7B;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,QAAQ,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAA;CAAE,CAkC3E;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAE3F;AAED;iEACiE;AACjE,eAAO,MAAM,eAAe,kCAAe,CAAA"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// ══════════════════════════════════════════════════════════════════
|
|
2
|
+
// Data Narrowing Invariant — Data Can Only Narrow Authority
|
|
3
|
+
// ══════════════════════════════════════════════════════════════════
|
|
4
|
+
// GPT's "Context-Bypass Attack" (consilium March 2026):
|
|
5
|
+
// An agent reads a data source containing authority-widening
|
|
6
|
+
// instructions. The gateway must ensure data inputs can ONLY narrow
|
|
7
|
+
// the ConstraintVector, never widen it.
|
|
8
|
+
//
|
|
9
|
+
// Same as monotonic narrowing for delegation — monotonic narrowing
|
|
10
|
+
// for data influence. Only signed delegations from a higher-tier
|
|
11
|
+
// principal can widen authority.
|
|
12
|
+
// ══════════════════════════════════════════════════════════════════
|
|
13
|
+
/** Status ordering for monotonic narrowing check.
|
|
14
|
+
* Lower number = more restrictive. Authority can only move DOWN. */
|
|
15
|
+
const STATUS_ORDER = {
|
|
16
|
+
'fail': 0,
|
|
17
|
+
'unknown': 1,
|
|
18
|
+
'not_applicable': 2,
|
|
19
|
+
'pass': 3,
|
|
20
|
+
};
|
|
21
|
+
/** Assert that data influence only narrows authority.
|
|
22
|
+
* Compares constraint evaluations BEFORE and AFTER data is considered.
|
|
23
|
+
* Any facet that moves from a more restrictive status to a more
|
|
24
|
+
* permissive one is a violation — data attempted to widen authority.
|
|
25
|
+
*
|
|
26
|
+
* @param before - Facet evaluations before data influence
|
|
27
|
+
* @param after - Facet evaluations after data influence
|
|
28
|
+
* @returns NarrowingCheckResult with any violations */
|
|
29
|
+
export function assertDataNarrowsOnly(before, after) {
|
|
30
|
+
const violations = [];
|
|
31
|
+
const beforeMap = new Map(before.map(f => [f.facet, f.status]));
|
|
32
|
+
for (const a of after) {
|
|
33
|
+
const beforeStatus = beforeMap.get(a.facet);
|
|
34
|
+
if (beforeStatus === undefined)
|
|
35
|
+
continue; // new facet, no comparison
|
|
36
|
+
const beforeOrder = STATUS_ORDER[beforeStatus];
|
|
37
|
+
const afterOrder = STATUS_ORDER[a.status];
|
|
38
|
+
// If after is MORE permissive (higher order) than before → violation
|
|
39
|
+
if (afterOrder > beforeOrder) {
|
|
40
|
+
violations.push({
|
|
41
|
+
facet: a.facet,
|
|
42
|
+
before: beforeStatus,
|
|
43
|
+
after: a.status,
|
|
44
|
+
message: `Data attempted to widen ${a.facet}: ${beforeStatus} → ${a.status}`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { valid: violations.length === 0, violations };
|
|
49
|
+
}
|
|
50
|
+
/** Apply data-sourced constraint modifications safely.
|
|
51
|
+
* Only allows narrowing (making more restrictive).
|
|
52
|
+
* Returns the narrowed snapshots with any widening attempts rejected.
|
|
53
|
+
*
|
|
54
|
+
* Use case: a data source declares "this data requires scope:read_only"
|
|
55
|
+
* → the constraint is narrowed. But if data declares "grant scope:admin"
|
|
56
|
+
* → the widening is rejected and the original constraint stands. */
|
|
57
|
+
export function applyDataConstraints(current, dataInfluence) {
|
|
58
|
+
const currentMap = new Map(current.map(f => [f.facet, f]));
|
|
59
|
+
const result = [...current];
|
|
60
|
+
const rejected = [];
|
|
61
|
+
for (const influence of dataInfluence) {
|
|
62
|
+
const existing = currentMap.get(influence.facet);
|
|
63
|
+
if (!existing) {
|
|
64
|
+
// New facet from data — only accept if restrictive (fail or unknown)
|
|
65
|
+
if (STATUS_ORDER[influence.status] <= STATUS_ORDER['unknown']) {
|
|
66
|
+
result.push(influence);
|
|
67
|
+
}
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const existingOrder = STATUS_ORDER[existing.status];
|
|
71
|
+
const influenceOrder = STATUS_ORDER[influence.status];
|
|
72
|
+
if (influenceOrder <= existingOrder) {
|
|
73
|
+
// More restrictive or equal — allowed (narrowing)
|
|
74
|
+
const idx = result.findIndex(f => f.facet === influence.facet);
|
|
75
|
+
if (idx >= 0)
|
|
76
|
+
result[idx] = influence;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Less restrictive — rejected (widening attempt)
|
|
80
|
+
rejected.push({
|
|
81
|
+
facet: influence.facet,
|
|
82
|
+
before: existing.status,
|
|
83
|
+
after: influence.status,
|
|
84
|
+
message: `Rejected: data tried to widen ${influence.facet} from ${existing.status} to ${influence.status}`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { result, rejected };
|
|
89
|
+
}
|
|
90
|
+
/** Check if a status transition is valid narrowing (same or more restrictive) */
|
|
91
|
+
export function isValidNarrowing(before, after) {
|
|
92
|
+
return STATUS_ORDER[after] <= STATUS_ORDER[before];
|
|
93
|
+
}
|
|
94
|
+
/** The status ordering: fail < unknown < not_applicable < pass.
|
|
95
|
+
* Exported for test vectors and cross-language verification. */
|
|
96
|
+
export const NARROWING_ORDER = STATUS_ORDER;
|
|
97
|
+
//# sourceMappingURL=data-narrowing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-narrowing.js","sourceRoot":"","sources":["../../../src/core/data-narrowing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ConstraintFacet, ConstraintFailure } from '../types/gateway.js';
|
|
2
|
+
/** Five operator-facing denial domains.
|
|
3
|
+
* Each groups 2-4 ConstraintVector facets. */
|
|
4
|
+
export type DenialDomain = 'identity_trust' | 'authority_scope' | 'economic' | 'temporal_integrity' | 'safety_values';
|
|
5
|
+
/** Structured denial summary for operators and dashboards.
|
|
6
|
+
* Primary reason + contributing factors + remediation hint. */
|
|
7
|
+
export interface DenialSummary {
|
|
8
|
+
/** The single most important reason the action was denied */
|
|
9
|
+
primary: {
|
|
10
|
+
domain: DenialDomain;
|
|
11
|
+
domainLabel: string;
|
|
12
|
+
facet: ConstraintFacet;
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
/** Additional failures beyond the primary */
|
|
17
|
+
contributing: Array<{
|
|
18
|
+
domain: DenialDomain;
|
|
19
|
+
facet: ConstraintFacet;
|
|
20
|
+
code: string;
|
|
21
|
+
}>;
|
|
22
|
+
/** Total number of failed facets */
|
|
23
|
+
totalFailures: number;
|
|
24
|
+
/** The nearest condition that would have satisfied the primary failure */
|
|
25
|
+
nearestSatisfiable?: string;
|
|
26
|
+
/** One-line remediation hint */
|
|
27
|
+
remediationHint?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Get the denial domain for a constraint facet */
|
|
30
|
+
export declare function getDomain(facet: ConstraintFacet): DenialDomain;
|
|
31
|
+
/** Get the human-readable label for a domain */
|
|
32
|
+
export declare function getDomainLabel(domain: DenialDomain): string;
|
|
33
|
+
/** Evaluation order: cheapest checks first.
|
|
34
|
+
* Gateway should evaluate in this order for deny-fast optimization.
|
|
35
|
+
* This also determines which failure is "primary" — first to fail wins. */
|
|
36
|
+
export declare const EVALUATION_ORDER: ConstraintFacet[];
|
|
37
|
+
/** Create a structured denial summary from constraint failures.
|
|
38
|
+
* The primary failure is the FIRST in evaluation order (cheapest check
|
|
39
|
+
* that failed), which is also the most actionable for the operator. */
|
|
40
|
+
export declare function summarizeDenial(failures: ConstraintFailure[]): DenialSummary | null;
|
|
41
|
+
/** Group failures by domain for dashboard display */
|
|
42
|
+
export declare function groupByDomain(failures: ConstraintFailure[]): Record<DenialDomain, ConstraintFailure[]>;
|
|
43
|
+
//# sourceMappingURL=denial-domains.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"denial-domains.d.ts","sourceRoot":"","sources":["../../../src/core/denial-domains.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAoB,MAAM,qBAAqB,CAAA;AAE/F;+CAC+C;AAC/C,MAAM,MAAM,YAAY,GACpB,gBAAgB,GAChB,iBAAiB,GACjB,UAAU,GACV,oBAAoB,GACpB,eAAe,CAAA;AA6BnB;gEACgE;AAChE,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,OAAO,EAAE;QACP,MAAM,EAAE,YAAY,CAAA;QACpB,WAAW,EAAE,MAAM,CAAA;QACnB,KAAK,EAAE,eAAe,CAAA;QACtB,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,6CAA6C;IAC7C,YAAY,EAAE,KAAK,CAAC;QAClB,MAAM,EAAE,YAAY,CAAA;QACpB,KAAK,EAAE,eAAe,CAAA;QACtB,IAAI,EAAE,MAAM,CAAA;KACb,CAAC,CAAA;IACF,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAA;IACrB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gCAAgC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,YAAY,CAE9D;AAED,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE3D;AAED;;4EAE4E;AAC5E,eAAO,MAAM,gBAAgB,EAAE,eAAe,EAe7C,CAAA;AAED;;wEAEwE;AACxE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,aAAa,GAAG,IAAI,CA8BnF;AAgDD,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,YAAY,EAAE,iBAAiB,EAAE,CAAC,CAStG"}
|