agent-passport-system 1.40.0 → 1.41.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 +9 -3
- package/dist/src/core/behavioral-fingerprint.d.ts +186 -0
- package/dist/src/core/behavioral-fingerprint.d.ts.map +1 -0
- package/dist/src/core/behavioral-fingerprint.js +214 -0
- package/dist/src/core/behavioral-fingerprint.js.map +1 -0
- package/dist/src/core/denial-domains.js +2 -2
- package/dist/src/core/probe-identity.d.ts +77 -0
- package/dist/src/core/probe-identity.d.ts.map +1 -0
- package/dist/src/core/probe-identity.js +102 -0
- package/dist/src/core/probe-identity.js.map +1 -0
- package/dist/src/core/reputation-authority.d.ts +198 -0
- package/dist/src/core/reputation-authority.d.ts.map +1 -1
- package/dist/src/core/reputation-authority.js +348 -4
- package/dist/src/core/reputation-authority.js.map +1 -1
- package/dist/src/core/time.d.ts +42 -1
- package/dist/src/core/time.d.ts.map +1 -1
- package/dist/src/core/time.js +103 -0
- package/dist/src/core/time.js.map +1 -1
- package/dist/src/index.d.ts +9 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +15 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/reputation-authority.d.ts +22 -0
- package/dist/src/types/reputation-authority.d.ts.map +1 -1
- package/dist/src/types/time.d.ts +14 -0
- package/dist/src/types/time.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
> **For AI agents:** visit [aeoess.com/llms.txt](https://aeoess.com/llms.txt) for machine-readable docs.
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**Enforcement and accountability layer for AI agents. Bring your own identity.**
|
|
11
11
|
|
|
12
|
-
Authority can only decrease at each transfer point. The gateway is both judge and executor. Every action produces a signed receipt.
|
|
12
|
+
Accepts did:key, did:web, SPIFFE SVIDs, OAuth tokens, and native did:aps. Authority can only decrease at each transfer point. The gateway is both judge and executor. Every action produces a signed receipt. Gateway evaluation under 2ms.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
npm install agent-passport-system
|
|
@@ -17,10 +17,16 @@ npm install agent-passport-system
|
|
|
17
17
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
|
+
Lead with the curated essentials. `agent-passport-system/core` exposes the ~25 functions that 90% of integrations need — identity, delegation, enforcement, commerce, reputation, key management. The full `agent-passport-system` root import is unchanged and backward compatible: pull from it when Core does not cover your case.
|
|
21
|
+
|
|
20
22
|
```typescript
|
|
21
23
|
import {
|
|
22
|
-
createPassport, createDelegation,
|
|
24
|
+
createPassport, createDelegation,
|
|
25
|
+
evaluateIntent, commercePreflight, generateKeyPair
|
|
23
26
|
} from 'agent-passport-system/core'
|
|
27
|
+
|
|
28
|
+
// Full 936-export API still available — use when Core does not cover your case.
|
|
29
|
+
// import { ... } from 'agent-passport-system'
|
|
24
30
|
```
|
|
25
31
|
|
|
26
32
|
## Core Protocol
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { FidelityAttestation } from '../types/gateway.js';
|
|
2
|
+
/** Reference to a PDR (Probabilistic Delegation Reliability) score event,
|
|
3
|
+
* as defined in Nanook PDR v2.19 §6.3. The pdr.score.v1 wire format is
|
|
4
|
+
* not yet a published spec; this interface mirrors the field set the paper
|
|
5
|
+
* attributes to NexusGuard's AIP implementation (aip_identity/pdr.py).
|
|
6
|
+
*
|
|
7
|
+
* This is an EXTERNAL reference — the SDK does not produce these scores,
|
|
8
|
+
* it only carries them inside a fingerprint envelope. The optional
|
|
9
|
+
* signature field is the score's own external signature (e.g. from the
|
|
10
|
+
* NexusGuard issuer); verifyBehavioralFingerprint does NOT validate it. */
|
|
11
|
+
export interface PDRScoreRef {
|
|
12
|
+
source: 'pdr.score.v1';
|
|
13
|
+
/** Composite PDR score in [0, 1]. Per Nanook §6.3: weighted combination of
|
|
14
|
+
* Calibration (0.5), Adaptation (0.2), Robustness (0.3). */
|
|
15
|
+
scoreOverall: number;
|
|
16
|
+
/** Calibration sub-score in [0, 1]. Jaccard similarity of claimed vs delivered. */
|
|
17
|
+
scoreCalibration: number;
|
|
18
|
+
/** Adaptation sub-score in [0, 1]. Temporal improvement slope. */
|
|
19
|
+
scoreAdaptation: number;
|
|
20
|
+
/** Robustness sub-score in [0, 1]. Condition-based variance. */
|
|
21
|
+
scoreRobustness: number;
|
|
22
|
+
/** Number of behavioral observations the score is computed over. */
|
|
23
|
+
observationCount: number;
|
|
24
|
+
/** Temporal spread of those observations, in days. */
|
|
25
|
+
windowDays: number;
|
|
26
|
+
/** Identifier of the issuing PDR scorer (e.g. 'nexusguard-aip-0.5.48'). */
|
|
27
|
+
issuer: string;
|
|
28
|
+
/** ISO 8601 timestamp of score issuance. */
|
|
29
|
+
issuedAt: string;
|
|
30
|
+
/** Optional external signature from the PDR issuer. NOT validated by
|
|
31
|
+
* verifyBehavioralFingerprint — see JSDoc on that function. */
|
|
32
|
+
signature?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Reference to a within-session constraint compliance score, in the style
|
|
35
|
+
* of Saebo et al. arXiv:2603.03456 ("Asymmetric Goal Drift in Coding Agents
|
|
36
|
+
* Under Value Conflict"). Measures whether the agent maintained its
|
|
37
|
+
* declared value hierarchy under context pressure across a session.
|
|
38
|
+
*
|
|
39
|
+
* Like PDRScoreRef, this is an external reference. The SDK does not
|
|
40
|
+
* produce Saebo scores; it carries them inside the envelope so consumers
|
|
41
|
+
* that want all three axes have one signed artifact to verify. */
|
|
42
|
+
export interface SaeboScoreRef {
|
|
43
|
+
source: 'saebo.constraint.v1';
|
|
44
|
+
/** Compliance score in [0, 1]: 1.0 = no violations across session,
|
|
45
|
+
* 0.0 = constraint violated on every turn. */
|
|
46
|
+
complianceScore: number;
|
|
47
|
+
/** Number of constraint violations observed in the session. */
|
|
48
|
+
violationCount: number;
|
|
49
|
+
/** Total turn count of the session being scored. */
|
|
50
|
+
sessionTurnCount: number;
|
|
51
|
+
/** Identifier of the issuing constraint scorer. */
|
|
52
|
+
issuer: string;
|
|
53
|
+
/** ISO 8601 timestamp of score issuance. */
|
|
54
|
+
issuedAt: string;
|
|
55
|
+
/** Optional external signature from the issuer. NOT validated by
|
|
56
|
+
* verifyBehavioralFingerprint. */
|
|
57
|
+
signature?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Three-axis behavioral measurement envelope. Always carries at least one
|
|
60
|
+
* HBB FidelityAttestation (axis 1). PDR and Saebo references are optional
|
|
61
|
+
* axes — a fingerprint with only fidelity is still well-formed. */
|
|
62
|
+
export interface BehavioralFingerprint {
|
|
63
|
+
subject: {
|
|
64
|
+
/** DID of the subject agent being measured (not the measurer). */
|
|
65
|
+
did: string;
|
|
66
|
+
/** LLM substrate identifier the subject was running on at measurement
|
|
67
|
+
* time (e.g. 'claude-sonnet-4', 'gpt-5-turbo'). */
|
|
68
|
+
substrate: string;
|
|
69
|
+
};
|
|
70
|
+
/** One or more HBB attestations. Multiple attestations are common when
|
|
71
|
+
* the substrate-swap protocol from Nanook §8.10 takes pre/post probes. */
|
|
72
|
+
fidelity: FidelityAttestation[];
|
|
73
|
+
/** Optional axis 2: PDR cross-session reliability reference. */
|
|
74
|
+
pdr_ref?: PDRScoreRef;
|
|
75
|
+
/** Optional axis 3: Saebo within-session constraint compliance reference. */
|
|
76
|
+
constraint_ref?: SaeboScoreRef;
|
|
77
|
+
/** Identifier of the party that composed and signed this envelope.
|
|
78
|
+
* May or may not be the same party that produced the inner
|
|
79
|
+
* FidelityAttestations (which carry their own measuredBy). */
|
|
80
|
+
measurerId: string;
|
|
81
|
+
/** Hex-encoded Ed25519 public key of the envelope signer. Embedded so
|
|
82
|
+
* third parties can verify offline given only the envelope. */
|
|
83
|
+
measurerPublicKey: string;
|
|
84
|
+
/** ISO 8601 timestamp of envelope signing. */
|
|
85
|
+
signedAt: string;
|
|
86
|
+
/** Hex-encoded Ed25519 signature over canonicalize({ subject, fidelity,
|
|
87
|
+
* pdr_ref, constraint_ref, measurerId, signedAt }). */
|
|
88
|
+
signature: string;
|
|
89
|
+
}
|
|
90
|
+
/** Result of verifyBehavioralFingerprint. All-or-nothing validity flag plus
|
|
91
|
+
* per-field breakdown for diagnostic visibility. */
|
|
92
|
+
export interface FingerprintVerificationResult {
|
|
93
|
+
/** True iff the envelope signature verifies AND every inner fidelity
|
|
94
|
+
* attestation signature verifies under the same public key. */
|
|
95
|
+
valid: boolean;
|
|
96
|
+
/** Whether the envelope-level signature verified against measurerPublicKey. */
|
|
97
|
+
envelopeSignatureValid: boolean;
|
|
98
|
+
/** One boolean per inner FidelityAttestation, in the same order as
|
|
99
|
+
* fp.fidelity. Each entry is the result of verifyFidelityAttestation
|
|
100
|
+
* against the provided measurerPublicKey. */
|
|
101
|
+
innerFidelitySignaturesValid: boolean[];
|
|
102
|
+
/** Human-readable diagnostic messages. Empty array on success. */
|
|
103
|
+
errors: string[];
|
|
104
|
+
}
|
|
105
|
+
/** Compose and sign a BehavioralFingerprint envelope.
|
|
106
|
+
*
|
|
107
|
+
* Requires at least one FidelityAttestation. The envelope carries pre-built
|
|
108
|
+
* fidelity attestations as-is — they retain their own measuredBy/signature
|
|
109
|
+
* fields and stay independently verifiable.
|
|
110
|
+
*
|
|
111
|
+
* The signing key is used for the envelope signature only. Inner fidelity
|
|
112
|
+
* attestations were signed earlier by their own measurers; this function
|
|
113
|
+
* does not re-sign them.
|
|
114
|
+
*
|
|
115
|
+
* @throws if fidelity is empty.
|
|
116
|
+
*
|
|
117
|
+
* Reference: Nanook PDR v2.19 §2.2, §8.10. Gap audit §5 rank 2.
|
|
118
|
+
*/
|
|
119
|
+
export declare function createBehavioralFingerprint(subject: {
|
|
120
|
+
did: string;
|
|
121
|
+
substrate: string;
|
|
122
|
+
}, fidelity: FidelityAttestation[], opts: {
|
|
123
|
+
pdr?: PDRScoreRef;
|
|
124
|
+
constraint?: SaeboScoreRef;
|
|
125
|
+
measurerId: string;
|
|
126
|
+
measurerPublicKey: string;
|
|
127
|
+
signingKey: string;
|
|
128
|
+
/** Override the signing timestamp. Test fixtures only. */
|
|
129
|
+
signedAt?: string;
|
|
130
|
+
}): BehavioralFingerprint;
|
|
131
|
+
/** Verify a BehavioralFingerprint envelope.
|
|
132
|
+
*
|
|
133
|
+
* Performs two checks:
|
|
134
|
+
* 1. Envelope signature: verify the envelope's own Ed25519 signature
|
|
135
|
+
* against the provided measurerPublicKey.
|
|
136
|
+
* 2. Inner fidelity signatures: for each FidelityAttestation in
|
|
137
|
+
* fp.fidelity, run verifyFidelityAttestation against the SAME
|
|
138
|
+
* measurerPublicKey. The per-attestation result is reported in
|
|
139
|
+
* innerFidelitySignaturesValid.
|
|
140
|
+
*
|
|
141
|
+
* Limitation by design: this function does NOT verify pdr_ref.signature or
|
|
142
|
+
* constraint_ref.signature. Those are external axes whose issuers may use
|
|
143
|
+
* different key schemes, signature formats, or canonicalization rules. If
|
|
144
|
+
* those fields carry signatures, callers must verify them out-of-band with
|
|
145
|
+
* the appropriate issuer's verification logic. The fingerprint envelope's
|
|
146
|
+
* own signature still covers the entire pdr_ref / constraint_ref content
|
|
147
|
+
* as JSON, so tampering with their fields will fail envelope verification.
|
|
148
|
+
*
|
|
149
|
+
* Limitation on multi-measurer fidelity attestations: if the inner
|
|
150
|
+
* attestations were signed by different keys than the envelope, their per-
|
|
151
|
+
* attestation entries in innerFidelitySignaturesValid will be false. The
|
|
152
|
+
* envelope itself is still validly signed; callers can re-verify failed
|
|
153
|
+
* inner attestations against the correct keys via verifyFidelityAttestation.
|
|
154
|
+
*
|
|
155
|
+
* The all-or-nothing valid flag is true iff envelope signature is valid
|
|
156
|
+
* AND every inner fidelity signature is valid under the same key.
|
|
157
|
+
*/
|
|
158
|
+
export declare function verifyBehavioralFingerprint(fp: BehavioralFingerprint, measurerPublicKey: string): FingerprintVerificationResult;
|
|
159
|
+
/** Pure projection helper for a BehavioralFingerprint. Surfaces the top-
|
|
160
|
+
* level scalar from each axis without combining them into a single number.
|
|
161
|
+
*
|
|
162
|
+
* The paper's three-axis orthogonality claim (Nanook PDR v2.19 §2.2) is
|
|
163
|
+
* untested. This SDK deliberately does NOT bake a combinator policy into
|
|
164
|
+
* the projection: callers that want a composite score must compute it
|
|
165
|
+
* themselves with their own weighting choices. The function returns the
|
|
166
|
+
* raw axis scalars and lets the consumer decide.
|
|
167
|
+
*
|
|
168
|
+
* - fidelityMean: arithmetic mean of fp.fidelity[*].fidelity.score.
|
|
169
|
+
* A simple mean is the right default because each FidelityAttestation
|
|
170
|
+
* represents one HBB probe under different conditions (e.g. pre vs
|
|
171
|
+
* post substrate swap). Confidence-weighting is available inside the
|
|
172
|
+
* individual SubstrateFidelity scoring path; the envelope projection
|
|
173
|
+
* treats each attestation as one observation.
|
|
174
|
+
*
|
|
175
|
+
* - pdrOverall: scoreOverall from the optional PDR reference, or undefined
|
|
176
|
+
* if no PDR axis is present.
|
|
177
|
+
*
|
|
178
|
+
* - constraintCompliance: complianceScore from the optional Saebo
|
|
179
|
+
* reference, or undefined if no constraint axis is present.
|
|
180
|
+
*/
|
|
181
|
+
export declare function composeFingerprintAxes(fp: BehavioralFingerprint): {
|
|
182
|
+
fidelityMean: number;
|
|
183
|
+
pdrOverall?: number;
|
|
184
|
+
constraintCompliance?: number;
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=behavioral-fingerprint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behavioral-fingerprint.d.ts","sourceRoot":"","sources":["../../../src/core/behavioral-fingerprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// ══════════════════════════════════════════════════════════════════
|
|
2
|
+
// Behavioral Fingerprint — Three-Axis Joint Measurement Envelope
|
|
3
|
+
// ══════════════════════════════════════════════════════════════════
|
|
4
|
+
// Composes three independent behavioral measurement axes into one signed
|
|
5
|
+
// artifact:
|
|
6
|
+
//
|
|
7
|
+
// Axis 1 (within-session authority-pressure fidelity):
|
|
8
|
+
// AEOESS Hold/Bend/Break — src/core/fidelity-probe.ts
|
|
9
|
+
//
|
|
10
|
+
// Axis 2 (cross-session output reliability):
|
|
11
|
+
// PDR score reference — produced by NexusGuard or any pdr.score.v1 issuer
|
|
12
|
+
//
|
|
13
|
+
// Axis 3 (within-session constraint compliance under value pressure):
|
|
14
|
+
// Saebo et al. constraint score reference — produced by an external scorer
|
|
15
|
+
//
|
|
16
|
+
// The envelope does NOT combine the three axes into a single composite
|
|
17
|
+
// score. The orthogonality claim from Nanook PDR v2.19 §2.2 is untested,
|
|
18
|
+
// and baking a combinator policy into the SDK would prejudge that result.
|
|
19
|
+
// composeFingerprintAxes() is a pure projection helper, nothing more.
|
|
20
|
+
//
|
|
21
|
+
// Signing: Ed25519. The envelope signature covers the canonical JSON of
|
|
22
|
+
// {subject, fidelity, pdr_ref, constraint_ref, measurerId, signedAt} —
|
|
23
|
+
// every field except the signature itself. Inner FidelityAttestation
|
|
24
|
+
// objects retain their own signatures (signed by their own measurer); the
|
|
25
|
+
// envelope signature covers them as JSON content but does not replace
|
|
26
|
+
// their independent verifiability.
|
|
27
|
+
//
|
|
28
|
+
// Reference: Nanook PDR v2.19 §2.2 (three-axis framework), §8.10 (joint
|
|
29
|
+
// experiment design), gap audit §3 row 10, gap audit §5 rank 2.
|
|
30
|
+
// ══════════════════════════════════════════════════════════════════
|
|
31
|
+
import { sign, verify } from '../crypto/keys.js';
|
|
32
|
+
import { canonicalize } from './canonical.js';
|
|
33
|
+
import { verifyFidelityAttestation } from './fidelity-probe.js';
|
|
34
|
+
// ── Create ──
|
|
35
|
+
/** Compose and sign a BehavioralFingerprint envelope.
|
|
36
|
+
*
|
|
37
|
+
* Requires at least one FidelityAttestation. The envelope carries pre-built
|
|
38
|
+
* fidelity attestations as-is — they retain their own measuredBy/signature
|
|
39
|
+
* fields and stay independently verifiable.
|
|
40
|
+
*
|
|
41
|
+
* The signing key is used for the envelope signature only. Inner fidelity
|
|
42
|
+
* attestations were signed earlier by their own measurers; this function
|
|
43
|
+
* does not re-sign them.
|
|
44
|
+
*
|
|
45
|
+
* @throws if fidelity is empty.
|
|
46
|
+
*
|
|
47
|
+
* Reference: Nanook PDR v2.19 §2.2, §8.10. Gap audit §5 rank 2.
|
|
48
|
+
*/
|
|
49
|
+
export function createBehavioralFingerprint(subject, fidelity, opts) {
|
|
50
|
+
if (!Array.isArray(fidelity) || fidelity.length === 0) {
|
|
51
|
+
throw new Error('createBehavioralFingerprint: at least one fidelity attestation is required');
|
|
52
|
+
}
|
|
53
|
+
if (!opts.measurerId || typeof opts.measurerId !== 'string') {
|
|
54
|
+
throw new Error('createBehavioralFingerprint: measurerId must be a non-empty string');
|
|
55
|
+
}
|
|
56
|
+
if (!opts.measurerPublicKey || typeof opts.measurerPublicKey !== 'string') {
|
|
57
|
+
throw new Error('createBehavioralFingerprint: measurerPublicKey must be a non-empty string');
|
|
58
|
+
}
|
|
59
|
+
if (!opts.signingKey || typeof opts.signingKey !== 'string') {
|
|
60
|
+
throw new Error('createBehavioralFingerprint: signingKey must be a non-empty string');
|
|
61
|
+
}
|
|
62
|
+
const signedAt = opts.signedAt ?? new Date().toISOString();
|
|
63
|
+
// Build the unsigned envelope. Canonicalize will sort keys alphabetically
|
|
64
|
+
// and strip null/undefined, so PDR/constraint absence produces the same
|
|
65
|
+
// bytes regardless of property declaration order.
|
|
66
|
+
const unsigned = {
|
|
67
|
+
subject,
|
|
68
|
+
fidelity,
|
|
69
|
+
...(opts.pdr ? { pdr_ref: opts.pdr } : {}),
|
|
70
|
+
...(opts.constraint ? { constraint_ref: opts.constraint } : {}),
|
|
71
|
+
measurerId: opts.measurerId,
|
|
72
|
+
measurerPublicKey: opts.measurerPublicKey,
|
|
73
|
+
signedAt,
|
|
74
|
+
};
|
|
75
|
+
const payload = canonicalize(unsigned);
|
|
76
|
+
const signature = sign(payload, opts.signingKey);
|
|
77
|
+
return {
|
|
78
|
+
...unsigned,
|
|
79
|
+
signature,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// ── Verify ──
|
|
83
|
+
/** Verify a BehavioralFingerprint envelope.
|
|
84
|
+
*
|
|
85
|
+
* Performs two checks:
|
|
86
|
+
* 1. Envelope signature: verify the envelope's own Ed25519 signature
|
|
87
|
+
* against the provided measurerPublicKey.
|
|
88
|
+
* 2. Inner fidelity signatures: for each FidelityAttestation in
|
|
89
|
+
* fp.fidelity, run verifyFidelityAttestation against the SAME
|
|
90
|
+
* measurerPublicKey. The per-attestation result is reported in
|
|
91
|
+
* innerFidelitySignaturesValid.
|
|
92
|
+
*
|
|
93
|
+
* Limitation by design: this function does NOT verify pdr_ref.signature or
|
|
94
|
+
* constraint_ref.signature. Those are external axes whose issuers may use
|
|
95
|
+
* different key schemes, signature formats, or canonicalization rules. If
|
|
96
|
+
* those fields carry signatures, callers must verify them out-of-band with
|
|
97
|
+
* the appropriate issuer's verification logic. The fingerprint envelope's
|
|
98
|
+
* own signature still covers the entire pdr_ref / constraint_ref content
|
|
99
|
+
* as JSON, so tampering with their fields will fail envelope verification.
|
|
100
|
+
*
|
|
101
|
+
* Limitation on multi-measurer fidelity attestations: if the inner
|
|
102
|
+
* attestations were signed by different keys than the envelope, their per-
|
|
103
|
+
* attestation entries in innerFidelitySignaturesValid will be false. The
|
|
104
|
+
* envelope itself is still validly signed; callers can re-verify failed
|
|
105
|
+
* inner attestations against the correct keys via verifyFidelityAttestation.
|
|
106
|
+
*
|
|
107
|
+
* The all-or-nothing valid flag is true iff envelope signature is valid
|
|
108
|
+
* AND every inner fidelity signature is valid under the same key.
|
|
109
|
+
*/
|
|
110
|
+
export function verifyBehavioralFingerprint(fp, measurerPublicKey) {
|
|
111
|
+
const errors = [];
|
|
112
|
+
// Validate input shape
|
|
113
|
+
if (!fp || typeof fp !== 'object') {
|
|
114
|
+
return {
|
|
115
|
+
valid: false,
|
|
116
|
+
envelopeSignatureValid: false,
|
|
117
|
+
innerFidelitySignaturesValid: [],
|
|
118
|
+
errors: ['fingerprint is not an object'],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (!Array.isArray(fp.fidelity) || fp.fidelity.length === 0) {
|
|
122
|
+
return {
|
|
123
|
+
valid: false,
|
|
124
|
+
envelopeSignatureValid: false,
|
|
125
|
+
innerFidelitySignaturesValid: [],
|
|
126
|
+
errors: ['fingerprint must carry at least one fidelity attestation'],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Step 1: envelope signature.
|
|
130
|
+
// Reconstruct the canonical payload exactly as createBehavioralFingerprint
|
|
131
|
+
// produced it: every field except `signature`. Note that the spread-with-
|
|
132
|
+
// conditional pattern in create produces no key for absent optional fields,
|
|
133
|
+
// and canonicalize() strips null/undefined, so the two paths agree.
|
|
134
|
+
const unsigned = {
|
|
135
|
+
subject: fp.subject,
|
|
136
|
+
fidelity: fp.fidelity,
|
|
137
|
+
...(fp.pdr_ref !== undefined ? { pdr_ref: fp.pdr_ref } : {}),
|
|
138
|
+
...(fp.constraint_ref !== undefined ? { constraint_ref: fp.constraint_ref } : {}),
|
|
139
|
+
measurerId: fp.measurerId,
|
|
140
|
+
measurerPublicKey: fp.measurerPublicKey,
|
|
141
|
+
signedAt: fp.signedAt,
|
|
142
|
+
};
|
|
143
|
+
const payload = canonicalize(unsigned);
|
|
144
|
+
let envelopeSignatureValid = false;
|
|
145
|
+
try {
|
|
146
|
+
envelopeSignatureValid = verify(payload, fp.signature, measurerPublicKey);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
envelopeSignatureValid = false;
|
|
150
|
+
}
|
|
151
|
+
if (!envelopeSignatureValid) {
|
|
152
|
+
errors.push('envelope signature failed verification');
|
|
153
|
+
}
|
|
154
|
+
// Step 2: each inner fidelity attestation under the SAME public key.
|
|
155
|
+
const innerFidelitySignaturesValid = fp.fidelity.map((att, i) => {
|
|
156
|
+
let ok = false;
|
|
157
|
+
try {
|
|
158
|
+
ok = verifyFidelityAttestation(att, measurerPublicKey);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
ok = false;
|
|
162
|
+
}
|
|
163
|
+
if (!ok) {
|
|
164
|
+
errors.push(`inner fidelity attestation ${i} (${att.attestationId ?? 'unknown'}) failed verification`);
|
|
165
|
+
}
|
|
166
|
+
return ok;
|
|
167
|
+
});
|
|
168
|
+
const allInnerValid = innerFidelitySignaturesValid.every(Boolean);
|
|
169
|
+
return {
|
|
170
|
+
valid: envelopeSignatureValid && allInnerValid,
|
|
171
|
+
envelopeSignatureValid,
|
|
172
|
+
innerFidelitySignaturesValid,
|
|
173
|
+
errors,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// ── Compose / Project ──
|
|
177
|
+
/** Pure projection helper for a BehavioralFingerprint. Surfaces the top-
|
|
178
|
+
* level scalar from each axis without combining them into a single number.
|
|
179
|
+
*
|
|
180
|
+
* The paper's three-axis orthogonality claim (Nanook PDR v2.19 §2.2) is
|
|
181
|
+
* untested. This SDK deliberately does NOT bake a combinator policy into
|
|
182
|
+
* the projection: callers that want a composite score must compute it
|
|
183
|
+
* themselves with their own weighting choices. The function returns the
|
|
184
|
+
* raw axis scalars and lets the consumer decide.
|
|
185
|
+
*
|
|
186
|
+
* - fidelityMean: arithmetic mean of fp.fidelity[*].fidelity.score.
|
|
187
|
+
* A simple mean is the right default because each FidelityAttestation
|
|
188
|
+
* represents one HBB probe under different conditions (e.g. pre vs
|
|
189
|
+
* post substrate swap). Confidence-weighting is available inside the
|
|
190
|
+
* individual SubstrateFidelity scoring path; the envelope projection
|
|
191
|
+
* treats each attestation as one observation.
|
|
192
|
+
*
|
|
193
|
+
* - pdrOverall: scoreOverall from the optional PDR reference, or undefined
|
|
194
|
+
* if no PDR axis is present.
|
|
195
|
+
*
|
|
196
|
+
* - constraintCompliance: complianceScore from the optional Saebo
|
|
197
|
+
* reference, or undefined if no constraint axis is present.
|
|
198
|
+
*/
|
|
199
|
+
export function composeFingerprintAxes(fp) {
|
|
200
|
+
if (!fp.fidelity || fp.fidelity.length === 0) {
|
|
201
|
+
throw new Error('composeFingerprintAxes: fingerprint has no fidelity attestations');
|
|
202
|
+
}
|
|
203
|
+
const sum = fp.fidelity.reduce((acc, att) => acc + att.fidelity.score, 0);
|
|
204
|
+
const fidelityMean = sum / fp.fidelity.length;
|
|
205
|
+
const result = {
|
|
206
|
+
fidelityMean,
|
|
207
|
+
};
|
|
208
|
+
if (fp.pdr_ref)
|
|
209
|
+
result.pdrOverall = fp.pdr_ref.scoreOverall;
|
|
210
|
+
if (fp.constraint_ref)
|
|
211
|
+
result.constraintCompliance = fp.constraint_ref.complianceScore;
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=behavioral-fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behavioral-fingerprint.js","sourceRoot":"","sources":["../../../src/core/behavioral-fingerprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// ══════════════════════════════════════════════════════════════════
|
|
2
2
|
// Denial Domains — Operator-Facing Constraint Grouping
|
|
3
3
|
// ══════════════════════════════════════════════════════════════════
|
|
4
|
-
// The ConstraintVector has
|
|
4
|
+
// The ConstraintVector has 14 facets. That's correct for the machine.
|
|
5
5
|
// For humans it's unusable. This module groups facets into 5 domains
|
|
6
6
|
// and provides a primary + contributing denial format.
|
|
7
7
|
//
|
|
8
|
-
// Consilium Priority 4 — unanimous that
|
|
8
|
+
// Consilium Priority 4 — unanimous that 14 facets need grouping.
|
|
9
9
|
// Claude: "primary denial reason + count"
|
|
10
10
|
// GPT: "4 master tiers"
|
|
11
11
|
// Gemini: "5 operator-facing domains"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/** A content-addressable identifier for an evaluation probe. The hash
|
|
2
|
+
* is the canonical JSON of the probe digested with the named algorithm.
|
|
3
|
+
* Two probes with the same content (regardless of property declaration
|
|
4
|
+
* order in the source) produce the same hash. */
|
|
5
|
+
export interface ProbeIdentity {
|
|
6
|
+
/** Hex-encoded digest. Length depends on algorithm: 64 for sha256, 32 for md5. */
|
|
7
|
+
hash: string;
|
|
8
|
+
/** Hash algorithm used. SHA-256 by default; MD5 for Nanook interop. */
|
|
9
|
+
algorithm: 'sha256' | 'md5';
|
|
10
|
+
/** ISO 8601 timestamp of when the hash was computed. The only clock read in this module. */
|
|
11
|
+
computedAt: string;
|
|
12
|
+
}
|
|
13
|
+
/** Result of verifying a probe against an expected hash. The function
|
|
14
|
+
* does NOT throw on mismatch; the caller decides what to do. */
|
|
15
|
+
export interface ProbeIdentityVerification {
|
|
16
|
+
/** True iff computedHash === expectedHash. */
|
|
17
|
+
match: boolean;
|
|
18
|
+
/** The hash the caller said the probe should have. */
|
|
19
|
+
expectedHash: string;
|
|
20
|
+
/** The hash actually computed from the probe. */
|
|
21
|
+
computedHash: string;
|
|
22
|
+
/** Algorithm used for the recomputation. */
|
|
23
|
+
algorithm: 'sha256' | 'md5';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Compute a content-addressable identity for an evaluation probe.
|
|
27
|
+
*
|
|
28
|
+
* The probe is canonicalized via canonicalize() from canonical.ts (RFC
|
|
29
|
+
* 8785 JCS) before hashing. This means two probes with the same content
|
|
30
|
+
* but different property declaration order produce identical hashes,
|
|
31
|
+
* which is the entire point of the utility — it lets a downstream
|
|
32
|
+
* scoring system prove that the probe it scored is byte-identical to the
|
|
33
|
+
* probe the issuer published, even after a JSON round-trip that may have
|
|
34
|
+
* reordered keys.
|
|
35
|
+
*
|
|
36
|
+
* Pure in algorithmic terms: same input + same algorithm always
|
|
37
|
+
* produces the same hash. The only side effect is the ISO timestamp
|
|
38
|
+
* captured in computedAt, which is set from new Date(). Callers that
|
|
39
|
+
* need fully deterministic output (e.g. test fixtures) should compare
|
|
40
|
+
* the hash field directly and ignore computedAt.
|
|
41
|
+
*
|
|
42
|
+
* @param probe Any JSON-serializable value. Typically a FidelityChallenge
|
|
43
|
+
* or similar evaluation probe object.
|
|
44
|
+
* @param opts.algorithm 'sha256' (default) or 'md5' for Nanook interop.
|
|
45
|
+
*
|
|
46
|
+
* Reference: Nanook PDR v2.19 §5.9, gap audit §3 row 16 / §5 rank 7.
|
|
47
|
+
*/
|
|
48
|
+
export declare function computeProbeIdentity(probe: unknown, opts?: {
|
|
49
|
+
algorithm?: 'sha256' | 'md5';
|
|
50
|
+
}): ProbeIdentity;
|
|
51
|
+
/**
|
|
52
|
+
* Verify that a probe matches an expected hash.
|
|
53
|
+
*
|
|
54
|
+
* Recomputes the hash from the probe via canonicalize() + the chosen
|
|
55
|
+
* algorithm and compares to the expected hash. Returns a verification
|
|
56
|
+
* result with both hashes exposed for diagnostic visibility.
|
|
57
|
+
*
|
|
58
|
+
* Does NOT throw on mismatch. Callers decide what to do with a false
|
|
59
|
+
* match: log it, deny the action, fall back to a different probe, etc.
|
|
60
|
+
* The function is purely informational.
|
|
61
|
+
*
|
|
62
|
+
* Algorithm mismatch case: if the caller passes an expectedHash that was
|
|
63
|
+
* produced under a different algorithm than the one passed via opts
|
|
64
|
+
* (e.g. expectedHash is a SHA-256 digest but opts.algorithm is 'md5'),
|
|
65
|
+
* the function recomputes under the requested algorithm and reports
|
|
66
|
+
* match: false. The function does not try to detect the mismatch
|
|
67
|
+
* automatically — the algorithm is a caller-provided assertion about how
|
|
68
|
+
* the expectedHash was originally computed.
|
|
69
|
+
*
|
|
70
|
+
* @param probe Any JSON-serializable value.
|
|
71
|
+
* @param expectedHash Hex-encoded digest the caller asserts the probe should produce.
|
|
72
|
+
* @param opts.algorithm Algorithm to use for the recomputation. Default 'sha256'.
|
|
73
|
+
*/
|
|
74
|
+
export declare function verifyProbeIdentity(probe: unknown, expectedHash: string, opts?: {
|
|
75
|
+
algorithm?: 'sha256' | 'md5';
|
|
76
|
+
}): ProbeIdentityVerification;
|
|
77
|
+
//# sourceMappingURL=probe-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-identity.d.ts","sourceRoot":"","sources":["../../../src/core/probe-identity.ts"],"names":[],"mappings":"AAoCA;;;kDAGkD;AAClD,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,SAAS,EAAE,QAAQ,GAAG,KAAK,CAAA;IAC3B,4FAA4F;IAC5F,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;iEACiE;AACjE,MAAM,WAAW,yBAAyB;IACxC,8CAA8C;IAC9C,KAAK,EAAE,OAAO,CAAA;IACd,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAA;IACpB,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,4CAA4C;IAC5C,SAAS,EAAE,QAAQ,GAAG,KAAK,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAAE,GACtC,aAAa,CASf;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;CAAE,GACtC,yBAAyB,CAU3B"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Copyright 2024-2026 Tymofii Pidlisnyi. Apache-2.0 license. See LICENSE.
|
|
2
|
+
// ══════════════════════════════════════════════════════════════════
|
|
3
|
+
// Probe Identity — Content-Addressable Hashing for Evaluation Probes
|
|
4
|
+
// ══════════════════════════════════════════════════════════════════
|
|
5
|
+
// Reference: Nanook PDR v2.19 §5.9 (MD5 hash prompt identity verification),
|
|
6
|
+
// gap audit §3 row 16 / §5 rank 7.
|
|
7
|
+
//
|
|
8
|
+
// Nanook §5.9 uses an MD5 hash of the evaluation probe prompt to
|
|
9
|
+
// eliminate input variation as a confound: every scoring run can prove
|
|
10
|
+
// it used byte-identical input. APS has canonical JSON serialization in
|
|
11
|
+
// canonical.ts but no probe-prompt identity verification protocol. This
|
|
12
|
+
// utility ships that protocol.
|
|
13
|
+
//
|
|
14
|
+
// Algorithm choice:
|
|
15
|
+
// APS defaults to SHA-256 because the rest of the SDK uses it
|
|
16
|
+
// (canonical.ts, signatures, content hashing). MD5 is available as an
|
|
17
|
+
// opt-in for interop with Nanook's existing probe bank. Callers pick
|
|
18
|
+
// via opts.algorithm. Both produce hex strings.
|
|
19
|
+
//
|
|
20
|
+
// Canonicalization:
|
|
21
|
+
// The point of this utility is that two JSON objects with the same
|
|
22
|
+
// content but different key declaration order produce the same hash.
|
|
23
|
+
// canonicalize() from canonical.ts (RFC 8785 JCS) guarantees this by
|
|
24
|
+
// sorting keys alphabetically and stripping null/undefined. Do NOT use
|
|
25
|
+
// JSON.stringify() directly — declaration order would leak through.
|
|
26
|
+
//
|
|
27
|
+
// Array semantics:
|
|
28
|
+
// canonicalize() preserves array order because array order is semantic
|
|
29
|
+
// in JSON. [1, 2, 3] and [3, 2, 1] produce different hashes. This is
|
|
30
|
+
// correct: a probe that asks the agent to compute things in a specific
|
|
31
|
+
// sequence should be a different probe if the sequence changes.
|
|
32
|
+
// ══════════════════════════════════════════════════════════════════
|
|
33
|
+
import { createHash } from 'node:crypto';
|
|
34
|
+
import { canonicalize } from './canonical.js';
|
|
35
|
+
/**
|
|
36
|
+
* Compute a content-addressable identity for an evaluation probe.
|
|
37
|
+
*
|
|
38
|
+
* The probe is canonicalized via canonicalize() from canonical.ts (RFC
|
|
39
|
+
* 8785 JCS) before hashing. This means two probes with the same content
|
|
40
|
+
* but different property declaration order produce identical hashes,
|
|
41
|
+
* which is the entire point of the utility — it lets a downstream
|
|
42
|
+
* scoring system prove that the probe it scored is byte-identical to the
|
|
43
|
+
* probe the issuer published, even after a JSON round-trip that may have
|
|
44
|
+
* reordered keys.
|
|
45
|
+
*
|
|
46
|
+
* Pure in algorithmic terms: same input + same algorithm always
|
|
47
|
+
* produces the same hash. The only side effect is the ISO timestamp
|
|
48
|
+
* captured in computedAt, which is set from new Date(). Callers that
|
|
49
|
+
* need fully deterministic output (e.g. test fixtures) should compare
|
|
50
|
+
* the hash field directly and ignore computedAt.
|
|
51
|
+
*
|
|
52
|
+
* @param probe Any JSON-serializable value. Typically a FidelityChallenge
|
|
53
|
+
* or similar evaluation probe object.
|
|
54
|
+
* @param opts.algorithm 'sha256' (default) or 'md5' for Nanook interop.
|
|
55
|
+
*
|
|
56
|
+
* Reference: Nanook PDR v2.19 §5.9, gap audit §3 row 16 / §5 rank 7.
|
|
57
|
+
*/
|
|
58
|
+
export function computeProbeIdentity(probe, opts) {
|
|
59
|
+
const algorithm = opts?.algorithm ?? 'sha256';
|
|
60
|
+
const canonical = canonicalize(probe);
|
|
61
|
+
const hash = createHash(algorithm).update(canonical).digest('hex');
|
|
62
|
+
return {
|
|
63
|
+
hash,
|
|
64
|
+
algorithm,
|
|
65
|
+
computedAt: new Date().toISOString(),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Verify that a probe matches an expected hash.
|
|
70
|
+
*
|
|
71
|
+
* Recomputes the hash from the probe via canonicalize() + the chosen
|
|
72
|
+
* algorithm and compares to the expected hash. Returns a verification
|
|
73
|
+
* result with both hashes exposed for diagnostic visibility.
|
|
74
|
+
*
|
|
75
|
+
* Does NOT throw on mismatch. Callers decide what to do with a false
|
|
76
|
+
* match: log it, deny the action, fall back to a different probe, etc.
|
|
77
|
+
* The function is purely informational.
|
|
78
|
+
*
|
|
79
|
+
* Algorithm mismatch case: if the caller passes an expectedHash that was
|
|
80
|
+
* produced under a different algorithm than the one passed via opts
|
|
81
|
+
* (e.g. expectedHash is a SHA-256 digest but opts.algorithm is 'md5'),
|
|
82
|
+
* the function recomputes under the requested algorithm and reports
|
|
83
|
+
* match: false. The function does not try to detect the mismatch
|
|
84
|
+
* automatically — the algorithm is a caller-provided assertion about how
|
|
85
|
+
* the expectedHash was originally computed.
|
|
86
|
+
*
|
|
87
|
+
* @param probe Any JSON-serializable value.
|
|
88
|
+
* @param expectedHash Hex-encoded digest the caller asserts the probe should produce.
|
|
89
|
+
* @param opts.algorithm Algorithm to use for the recomputation. Default 'sha256'.
|
|
90
|
+
*/
|
|
91
|
+
export function verifyProbeIdentity(probe, expectedHash, opts) {
|
|
92
|
+
const algorithm = opts?.algorithm ?? 'sha256';
|
|
93
|
+
const canonical = canonicalize(probe);
|
|
94
|
+
const computedHash = createHash(algorithm).update(canonical).digest('hex');
|
|
95
|
+
return {
|
|
96
|
+
match: computedHash === expectedHash,
|
|
97
|
+
expectedHash,
|
|
98
|
+
computedHash,
|
|
99
|
+
algorithm,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=probe-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-identity.js","sourceRoot":"","sources":["../../../src/core/probe-identity.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qEAAqE;AACrE,qEAAqE;AACrE,qEAAqE;AACrE,4EAA4E;AAC5E,mCAAmC;AACnC,EAAE;AACF,iEAAiE;AACjE,uEAAuE;AACvE,wEAAwE;AACxE,wEAAwE;AACxE,+BAA+B;AAC/B,EAAE;AACF,oBAAoB;AACpB,gEAAgE;AAChE,wEAAwE;AACxE,uEAAuE;AACvE,kDAAkD;AAClD,EAAE;AACF,oBAAoB;AACpB,qEAAqE;AACrE,uEAAuE;AACvE,uEAAuE;AACvE,yEAAyE;AACzE,sEAAsE;AACtE,EAAE;AACF,mBAAmB;AACnB,yEAAyE;AACzE,uEAAuE;AACvE,yEAAyE;AACzE,kEAAkE;AAClE,qEAAqE;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AA4B7C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAc,EACd,IAAuC;IAEvC,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,QAAQ,CAAA;IAC7C,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAClE,OAAO;QACL,IAAI;QACJ,SAAS;QACT,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAc,EACd,YAAoB,EACpB,IAAuC;IAEvC,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,QAAQ,CAAA;IAC7C,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;IACrC,MAAM,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC1E,OAAO;QACL,KAAK,EAAE,YAAY,KAAK,YAAY;QACpC,YAAY;QACZ,YAAY;QACZ,SAAS;KACV,CAAA;AACH,CAAC"}
|