@scopeblind/passport 0.3.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 +184 -0
- package/dist/browser.d.mts +44 -0
- package/dist/browser.d.ts +44 -0
- package/dist/browser.js +180 -0
- package/dist/browser.mjs +148 -0
- package/dist/index.d.mts +421 -0
- package/dist/index.d.ts +421 -0
- package/dist/index.js +630 -0
- package/dist/index.mjs +568 -0
- package/dist/types-DHZHv2EE.d.mts +278 -0
- package/dist/types-DHZHv2EE.d.ts +278 -0
- package/package.json +63 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
// src/keys.ts
|
|
2
|
+
import { ed25519 } from "@noble/curves/ed25519";
|
|
3
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
4
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
5
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
6
|
+
function base58Encode(bytes) {
|
|
7
|
+
let num = BigInt("0x" + bytesToHex(bytes));
|
|
8
|
+
const chars = [];
|
|
9
|
+
while (num > 0n) {
|
|
10
|
+
const remainder = Number(num % 58n);
|
|
11
|
+
chars.unshift(BASE58_ALPHABET[remainder]);
|
|
12
|
+
num = num / 58n;
|
|
13
|
+
}
|
|
14
|
+
for (const b of bytes) {
|
|
15
|
+
if (b === 0) chars.unshift("1");
|
|
16
|
+
else break;
|
|
17
|
+
}
|
|
18
|
+
return chars.join("") || "1";
|
|
19
|
+
}
|
|
20
|
+
function derivePassportId(publicKey, role) {
|
|
21
|
+
const fingerprint = base58Encode(publicKey).slice(0, 12);
|
|
22
|
+
return `sb:${role}:${fingerprint}`;
|
|
23
|
+
}
|
|
24
|
+
function generatePassportKey(role) {
|
|
25
|
+
const secretKeyRaw = ed25519.utils.randomPrivateKey();
|
|
26
|
+
const publicKey = ed25519.getPublicKey(secretKeyRaw);
|
|
27
|
+
const secretKey = new Uint8Array(64);
|
|
28
|
+
secretKey.set(secretKeyRaw, 0);
|
|
29
|
+
secretKey.set(publicKey, 32);
|
|
30
|
+
const kid = derivePassportId(publicKey, role);
|
|
31
|
+
return {
|
|
32
|
+
publicKey,
|
|
33
|
+
secretKey,
|
|
34
|
+
kid,
|
|
35
|
+
role,
|
|
36
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function getSigningKey(keyPair) {
|
|
40
|
+
return bytesToHex(keyPair.secretKey.slice(0, 32));
|
|
41
|
+
}
|
|
42
|
+
function getVerifyKey(keyPair) {
|
|
43
|
+
return bytesToHex(keyPair.publicKey);
|
|
44
|
+
}
|
|
45
|
+
function hashString(value) {
|
|
46
|
+
const encoder = new TextEncoder();
|
|
47
|
+
return bytesToHex(sha256(encoder.encode(value)));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/manifest.ts
|
|
51
|
+
import { ed25519 as ed255192 } from "@noble/curves/ed25519";
|
|
52
|
+
import { sha256 as sha2562 } from "@noble/hashes/sha256";
|
|
53
|
+
import { bytesToHex as bytesToHex2, utf8ToBytes } from "@noble/hashes/utils";
|
|
54
|
+
function canonicalize(obj) {
|
|
55
|
+
return JSON.stringify(obj, (_key, value) => {
|
|
56
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
57
|
+
const sorted = {};
|
|
58
|
+
for (const k of Object.keys(value).sort()) {
|
|
59
|
+
sorted[k] = value[k];
|
|
60
|
+
}
|
|
61
|
+
return sorted;
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function canonicalHash(obj) {
|
|
67
|
+
return bytesToHex2(sha2562(utf8ToBytes(canonicalize(obj))));
|
|
68
|
+
}
|
|
69
|
+
function signPayload(payload, keyPair) {
|
|
70
|
+
const message = utf8ToBytes(canonicalize(payload));
|
|
71
|
+
const sigBytes = ed255192.sign(message, keyPair.secretKey.slice(0, 32));
|
|
72
|
+
return {
|
|
73
|
+
payload,
|
|
74
|
+
signature: {
|
|
75
|
+
alg: "EdDSA",
|
|
76
|
+
kid: keyPair.kid,
|
|
77
|
+
sig: bytesToHex2(sigBytes)
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function createCoachManifest(keyPair, input, previousVersion, version) {
|
|
82
|
+
const manifest = {
|
|
83
|
+
type: "scopeblind:coach-manifest",
|
|
84
|
+
id: keyPair.kid,
|
|
85
|
+
version: version || "1.0.0",
|
|
86
|
+
previous_version: previousVersion ?? null,
|
|
87
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
88
|
+
public_key: base58Encode(keyPair.publicKey),
|
|
89
|
+
display_name: input.display_name.trim().slice(0, 32)
|
|
90
|
+
};
|
|
91
|
+
return signPayload(manifest, keyPair);
|
|
92
|
+
}
|
|
93
|
+
function createAgentManifest(keyPair, input, previousVersion, version) {
|
|
94
|
+
const promptHash = hashString(input.configuration_attestations.system_prompt);
|
|
95
|
+
const manifest = {
|
|
96
|
+
type: "scopeblind:agent-manifest",
|
|
97
|
+
id: keyPair.kid,
|
|
98
|
+
version: version || "1.0.0",
|
|
99
|
+
previous_version: previousVersion ?? null,
|
|
100
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
101
|
+
public_key: base58Encode(keyPair.publicKey),
|
|
102
|
+
public_profile: input.public_profile,
|
|
103
|
+
configuration_attestations: {
|
|
104
|
+
model_family_hash: input.configuration_attestations.model_family_hash,
|
|
105
|
+
memory_mode: input.configuration_attestations.memory_mode,
|
|
106
|
+
prompt_hash: promptHash
|
|
107
|
+
},
|
|
108
|
+
capability_declarations: input.capability_declarations,
|
|
109
|
+
evidence_pointers: input.evidence_pointers || {}
|
|
110
|
+
};
|
|
111
|
+
return signPayload(manifest, keyPair);
|
|
112
|
+
}
|
|
113
|
+
function createOwnershipAttestation(coachKeyPair, agentId, agentManifestVersion) {
|
|
114
|
+
const attestation = {
|
|
115
|
+
type: "scopeblind:ownership-attestation",
|
|
116
|
+
coach_id: coachKeyPair.kid,
|
|
117
|
+
agent_id: agentId,
|
|
118
|
+
agent_manifest_version: agentManifestVersion,
|
|
119
|
+
granted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
120
|
+
};
|
|
121
|
+
return signPayload(attestation, coachKeyPair);
|
|
122
|
+
}
|
|
123
|
+
function createStatusRecord(signerKeyPair, targetId, status, reason) {
|
|
124
|
+
const record = {
|
|
125
|
+
type: "scopeblind:status-record",
|
|
126
|
+
target_id: targetId,
|
|
127
|
+
status,
|
|
128
|
+
changed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
129
|
+
issuer_id: signerKeyPair.kid,
|
|
130
|
+
...reason ? { reason } : {}
|
|
131
|
+
};
|
|
132
|
+
return signPayload(record, signerKeyPair);
|
|
133
|
+
}
|
|
134
|
+
function createCoachContribution(coachKeyPair, battleId, coachedSide, noteHash, noteLength, upliftVerdict) {
|
|
135
|
+
const contribution = {
|
|
136
|
+
type: "scopeblind:coach-contribution",
|
|
137
|
+
battle_id: battleId,
|
|
138
|
+
coach_id: coachKeyPair.kid,
|
|
139
|
+
coached_side: coachedSide,
|
|
140
|
+
note_hash: noteHash,
|
|
141
|
+
note_length: noteLength,
|
|
142
|
+
...upliftVerdict ? { uplift_verdict: upliftVerdict } : {}
|
|
143
|
+
};
|
|
144
|
+
return signPayload(contribution, coachKeyPair);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/verify.ts
|
|
148
|
+
import { ed25519 as ed255193 } from "@noble/curves/ed25519";
|
|
149
|
+
import { hexToBytes as hexToBytes2, utf8ToBytes as utf8ToBytes2 } from "@noble/hashes/utils";
|
|
150
|
+
function verifyEnvelope(envelope, expectedPublicKey) {
|
|
151
|
+
try {
|
|
152
|
+
if (!envelope.signature?.sig || !envelope.signature?.kid) {
|
|
153
|
+
return { valid: false, error: "missing_signature" };
|
|
154
|
+
}
|
|
155
|
+
const message = utf8ToBytes2(canonicalize(envelope.payload));
|
|
156
|
+
const hash = canonicalHash(envelope.payload);
|
|
157
|
+
const payload = envelope.payload;
|
|
158
|
+
let publicKeyBytes;
|
|
159
|
+
if (expectedPublicKey) {
|
|
160
|
+
publicKeyBytes = expectedPublicKey;
|
|
161
|
+
} else if (typeof payload.public_key === "string") {
|
|
162
|
+
publicKeyBytes = base58Decode(payload.public_key);
|
|
163
|
+
} else {
|
|
164
|
+
return { valid: false, error: "no_public_key_available" };
|
|
165
|
+
}
|
|
166
|
+
if (expectedPublicKey) {
|
|
167
|
+
const expectedKid = base58Encode(expectedPublicKey);
|
|
168
|
+
const signerFingerprint = envelope.signature.kid.split(":")[2];
|
|
169
|
+
const expectedFingerprint = expectedKid.slice(0, 12);
|
|
170
|
+
if (signerFingerprint !== expectedFingerprint) {
|
|
171
|
+
return { valid: false, hash, error: "kid_mismatch" };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const valid = ed255193.verify(
|
|
175
|
+
hexToBytes2(envelope.signature.sig),
|
|
176
|
+
message,
|
|
177
|
+
publicKeyBytes
|
|
178
|
+
);
|
|
179
|
+
return valid ? { valid: true, hash } : { valid: false, hash, error: "invalid_signature" };
|
|
180
|
+
} catch (err) {
|
|
181
|
+
return {
|
|
182
|
+
valid: false,
|
|
183
|
+
error: `verification_error: ${err instanceof Error ? err.message : "unknown"}`
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function verifyManifest(envelope) {
|
|
188
|
+
return verifyEnvelope(envelope);
|
|
189
|
+
}
|
|
190
|
+
function verifyOwnership(attestation, coachPublicKey) {
|
|
191
|
+
const result = verifyEnvelope(attestation, coachPublicKey);
|
|
192
|
+
if (!result.valid) return result;
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
function base58Decode(str) {
|
|
196
|
+
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
197
|
+
let num = 0n;
|
|
198
|
+
for (const char of str) {
|
|
199
|
+
const idx = ALPHABET.indexOf(char);
|
|
200
|
+
if (idx === -1) throw new Error(`Invalid base58 character: ${char}`);
|
|
201
|
+
num = num * 58n + BigInt(idx);
|
|
202
|
+
}
|
|
203
|
+
const hex = num.toString(16).padStart(64, "0");
|
|
204
|
+
const bytes = new Uint8Array(32);
|
|
205
|
+
for (let i = 0; i < 32; i++) {
|
|
206
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
207
|
+
}
|
|
208
|
+
return bytes;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/display.ts
|
|
212
|
+
function formatKidShort(kid) {
|
|
213
|
+
const parts = kid.split(":");
|
|
214
|
+
const fingerprint = parts[2] || kid;
|
|
215
|
+
if (fingerprint.length <= 8) return fingerprint;
|
|
216
|
+
return `${fingerprint.slice(0, 4)}\u2026${fingerprint.slice(-4)}`;
|
|
217
|
+
}
|
|
218
|
+
function formatKidLabeled(kid) {
|
|
219
|
+
const parts = kid.split(":");
|
|
220
|
+
const role = parts[1] === "coach" ? "Coach" : parts[1] === "agent" ? "Agent" : "Unknown";
|
|
221
|
+
return `${role} ${formatKidShort(kid)}`;
|
|
222
|
+
}
|
|
223
|
+
function getCoachSummary(envelope) {
|
|
224
|
+
const m = envelope.payload;
|
|
225
|
+
return {
|
|
226
|
+
kid: m.id,
|
|
227
|
+
displayName: m.display_name,
|
|
228
|
+
shortId: formatKidShort(m.id),
|
|
229
|
+
version: m.version,
|
|
230
|
+
createdAt: m.created_at
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function getAgentSummary(envelope) {
|
|
234
|
+
const m = envelope.payload;
|
|
235
|
+
return {
|
|
236
|
+
kid: m.id,
|
|
237
|
+
name: m.public_profile.name,
|
|
238
|
+
shortId: formatKidShort(m.id),
|
|
239
|
+
version: m.version,
|
|
240
|
+
domainLanes: m.public_profile.domain_lanes,
|
|
241
|
+
createdAt: m.created_at
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function isCoachManifest(manifest) {
|
|
245
|
+
return manifest.type === "scopeblind:coach-manifest";
|
|
246
|
+
}
|
|
247
|
+
function isAgentManifest(manifest) {
|
|
248
|
+
return manifest.type === "scopeblind:agent-manifest";
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/issuer.ts
|
|
252
|
+
import { ed25519 as ed255194 } from "@noble/curves/ed25519";
|
|
253
|
+
import { bytesToHex as bytesToHex3, hexToBytes as hexToBytes3, utf8ToBytes as utf8ToBytes3 } from "@noble/hashes/utils";
|
|
254
|
+
function generateIssuerKey() {
|
|
255
|
+
const secretKeyRaw = ed255194.utils.randomPrivateKey();
|
|
256
|
+
const publicKey = ed255194.getPublicKey(secretKeyRaw);
|
|
257
|
+
const fingerprint = base58Encode(publicKey).slice(0, 12);
|
|
258
|
+
return {
|
|
259
|
+
publicKeyHex: bytesToHex3(publicKey),
|
|
260
|
+
secretKeyHex: bytesToHex3(secretKeyRaw),
|
|
261
|
+
issuerId: `sb:issuer:${fingerprint}`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function signReceipt(payload, issuerSecretKeyHex, issuerId) {
|
|
265
|
+
const message = utf8ToBytes3(canonicalize(payload));
|
|
266
|
+
const sigBytes = ed255194.sign(message, hexToBytes3(issuerSecretKeyHex));
|
|
267
|
+
return {
|
|
268
|
+
payload,
|
|
269
|
+
signature: {
|
|
270
|
+
alg: "EdDSA",
|
|
271
|
+
kid: issuerId,
|
|
272
|
+
sig: bytesToHex3(sigBytes)
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function createArenaBattleReceipt(input, issuerSecretKeyHex, issuerId) {
|
|
277
|
+
const receipt = {
|
|
278
|
+
type: "blindllm:arena-battle",
|
|
279
|
+
battle_id: input.battleId,
|
|
280
|
+
lane_id: input.laneId,
|
|
281
|
+
agent_a: input.agentA,
|
|
282
|
+
agent_b: input.agentB,
|
|
283
|
+
winner: input.winner,
|
|
284
|
+
issued_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
285
|
+
issuer_id: issuerId,
|
|
286
|
+
...input.coach ? { coach: input.coach } : {}
|
|
287
|
+
};
|
|
288
|
+
return signReceipt(receipt, issuerSecretKeyHex, issuerId);
|
|
289
|
+
}
|
|
290
|
+
function createCoachUpliftReceipt(input, issuerSecretKeyHex, issuerId) {
|
|
291
|
+
const receipt = {
|
|
292
|
+
type: "blindllm:coach-uplift",
|
|
293
|
+
battle_id: input.battleId,
|
|
294
|
+
coach_id: input.coachId,
|
|
295
|
+
coached_side: input.coachedSide,
|
|
296
|
+
uplift_verdict: input.upliftVerdict,
|
|
297
|
+
issued_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
298
|
+
issuer_id: issuerId
|
|
299
|
+
};
|
|
300
|
+
return signReceipt(receipt, issuerSecretKeyHex, issuerId);
|
|
301
|
+
}
|
|
302
|
+
function createFormalDebateReceipt(input, issuerSecretKeyHex, issuerId) {
|
|
303
|
+
const receipt = {
|
|
304
|
+
type: "blindllm:formal-debate",
|
|
305
|
+
debate_id: input.debateId,
|
|
306
|
+
spec_id: input.specId,
|
|
307
|
+
lane_id: input.laneId,
|
|
308
|
+
artifact_hash: input.artifactHash,
|
|
309
|
+
resolution_hash: input.resolutionHash,
|
|
310
|
+
pro: input.pro,
|
|
311
|
+
con: input.con,
|
|
312
|
+
audience_winner: input.audienceWinner,
|
|
313
|
+
judge_winner: input.judgeWinner,
|
|
314
|
+
restraint_result: input.restraintResult,
|
|
315
|
+
issued_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
316
|
+
issuer_id: issuerId
|
|
317
|
+
};
|
|
318
|
+
return signReceipt(receipt, issuerSecretKeyHex, issuerId);
|
|
319
|
+
}
|
|
320
|
+
function hexToBase64url(hex) {
|
|
321
|
+
const bytes = hexToBytes3(hex);
|
|
322
|
+
const binary = String.fromCharCode(...bytes);
|
|
323
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
324
|
+
}
|
|
325
|
+
function buildPublicKeyEndpoint(issuerPublicKeyHex, issuerId) {
|
|
326
|
+
return {
|
|
327
|
+
keys: [{
|
|
328
|
+
kty: "OKP",
|
|
329
|
+
crv: "Ed25519",
|
|
330
|
+
kid: issuerId,
|
|
331
|
+
x: hexToBase64url(issuerPublicKeyHex),
|
|
332
|
+
use: "sig"
|
|
333
|
+
}],
|
|
334
|
+
issuer: "blindllm",
|
|
335
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function deriveIssuerFromSeed(secretKeyHex) {
|
|
339
|
+
const secretKeyBytes = hexToBytes3(secretKeyHex);
|
|
340
|
+
const publicKey = ed255194.getPublicKey(secretKeyBytes);
|
|
341
|
+
const fingerprint = base58Encode(publicKey).slice(0, 12);
|
|
342
|
+
return {
|
|
343
|
+
publicKeyHex: bytesToHex3(publicKey),
|
|
344
|
+
secretKeyHex,
|
|
345
|
+
issuerId: `sb:issuer:${fingerprint}`
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/portable.ts
|
|
350
|
+
import { bytesToHex as bytesToHex4, hexToBytes as hexToBytes4 } from "@noble/hashes/utils";
|
|
351
|
+
import { ed25519 as ed255195 } from "@noble/curves/ed25519";
|
|
352
|
+
function exportPassportBundle(bundle) {
|
|
353
|
+
return {
|
|
354
|
+
v: 1,
|
|
355
|
+
exported_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
356
|
+
passport: {
|
|
357
|
+
kid: bundle.key.kid,
|
|
358
|
+
role: bundle.key.role,
|
|
359
|
+
created_at: bundle.key.created_at,
|
|
360
|
+
publicKeyHex: bytesToHex4(bundle.key.publicKey),
|
|
361
|
+
secretKeyHex: bytesToHex4(bundle.key.secretKey)
|
|
362
|
+
},
|
|
363
|
+
manifests: bundle.manifests,
|
|
364
|
+
ownership_attestations: bundle.ownership_attestations,
|
|
365
|
+
status_records: bundle.status_records
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function serializeBundle(bundle) {
|
|
369
|
+
return JSON.stringify(bundle, null, 2);
|
|
370
|
+
}
|
|
371
|
+
function importPassportBundle(json) {
|
|
372
|
+
const errors = [];
|
|
373
|
+
const warnings = [];
|
|
374
|
+
let raw;
|
|
375
|
+
try {
|
|
376
|
+
raw = JSON.parse(json);
|
|
377
|
+
} catch {
|
|
378
|
+
return { valid: false, errors: ["Invalid JSON"], warnings };
|
|
379
|
+
}
|
|
380
|
+
const data = raw;
|
|
381
|
+
if (data.v !== 1) {
|
|
382
|
+
errors.push(`Unsupported bundle version: ${data.v}`);
|
|
383
|
+
return { valid: false, errors, warnings };
|
|
384
|
+
}
|
|
385
|
+
const passport = data.passport;
|
|
386
|
+
if (!passport) {
|
|
387
|
+
errors.push("Missing passport field");
|
|
388
|
+
return { valid: false, errors, warnings };
|
|
389
|
+
}
|
|
390
|
+
if (typeof passport.publicKeyHex !== "string" || typeof passport.secretKeyHex !== "string") {
|
|
391
|
+
errors.push("Missing or invalid key hex values");
|
|
392
|
+
return { valid: false, errors, warnings };
|
|
393
|
+
}
|
|
394
|
+
if (typeof passport.kid !== "string" || !passport.kid.startsWith("sb:")) {
|
|
395
|
+
errors.push("Invalid passport kid format");
|
|
396
|
+
return { valid: false, errors, warnings };
|
|
397
|
+
}
|
|
398
|
+
const role = passport.role;
|
|
399
|
+
if (role !== "coach" && role !== "agent") {
|
|
400
|
+
errors.push(`Invalid role: ${role}`);
|
|
401
|
+
return { valid: false, errors, warnings };
|
|
402
|
+
}
|
|
403
|
+
let publicKey;
|
|
404
|
+
let secretKey;
|
|
405
|
+
try {
|
|
406
|
+
publicKey = hexToBytes4(passport.publicKeyHex);
|
|
407
|
+
secretKey = hexToBytes4(passport.secretKeyHex);
|
|
408
|
+
} catch {
|
|
409
|
+
errors.push("Failed to decode key hex values");
|
|
410
|
+
return { valid: false, errors, warnings };
|
|
411
|
+
}
|
|
412
|
+
if (publicKey.length !== 32) {
|
|
413
|
+
errors.push(`Invalid public key length: ${publicKey.length} (expected 32)`);
|
|
414
|
+
return { valid: false, errors, warnings };
|
|
415
|
+
}
|
|
416
|
+
if (secretKey.length !== 64) {
|
|
417
|
+
errors.push(`Invalid secret key length: ${secretKey.length} (expected 64)`);
|
|
418
|
+
return { valid: false, errors, warnings };
|
|
419
|
+
}
|
|
420
|
+
const seed = secretKey.slice(0, 32);
|
|
421
|
+
let derivedPublicKey;
|
|
422
|
+
try {
|
|
423
|
+
derivedPublicKey = ed255195.getPublicKey(seed);
|
|
424
|
+
} catch {
|
|
425
|
+
errors.push("Secret key seed is not a valid Ed25519 private key");
|
|
426
|
+
return { valid: false, errors, warnings };
|
|
427
|
+
}
|
|
428
|
+
if (bytesToHex4(derivedPublicKey) !== bytesToHex4(publicKey)) {
|
|
429
|
+
errors.push("Key coherence failure: secret key does not derive to the claimed public key");
|
|
430
|
+
return { valid: false, errors, warnings };
|
|
431
|
+
}
|
|
432
|
+
const expectedKid = derivePassportId(publicKey, role);
|
|
433
|
+
if (passport.kid !== expectedKid) {
|
|
434
|
+
errors.push(`Kid mismatch: bundle claims ${passport.kid} but public key derives to ${expectedKid}`);
|
|
435
|
+
return { valid: false, errors, warnings };
|
|
436
|
+
}
|
|
437
|
+
const key = {
|
|
438
|
+
publicKey,
|
|
439
|
+
secretKey,
|
|
440
|
+
kid: passport.kid,
|
|
441
|
+
role,
|
|
442
|
+
created_at: passport.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
443
|
+
};
|
|
444
|
+
const rawManifests = Array.isArray(data.manifests) ? data.manifests : [];
|
|
445
|
+
const validManifests = [];
|
|
446
|
+
for (const m of rawManifests) {
|
|
447
|
+
const result = verifyManifest(m);
|
|
448
|
+
if (!result.valid) {
|
|
449
|
+
errors.push(`Manifest ${m.payload?.id || "unknown"} has invalid signature: ${result.error}`);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
if (m.payload?.id && m.payload.id !== key.kid) {
|
|
453
|
+
warnings.push(`Dropped manifest ${m.payload.id}: does not belong to passport ${key.kid}`);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
validManifests.push(m);
|
|
457
|
+
}
|
|
458
|
+
const bundle = {
|
|
459
|
+
key,
|
|
460
|
+
manifests: validManifests,
|
|
461
|
+
ownership_attestations: Array.isArray(data.ownership_attestations) ? data.ownership_attestations : [],
|
|
462
|
+
status_records: Array.isArray(data.status_records) ? data.status_records : []
|
|
463
|
+
};
|
|
464
|
+
return {
|
|
465
|
+
valid: errors.length === 0,
|
|
466
|
+
bundle,
|
|
467
|
+
errors,
|
|
468
|
+
warnings
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/artifact-bridge.ts
|
|
473
|
+
function passportToArtifact(envelope, options) {
|
|
474
|
+
return {
|
|
475
|
+
version: "2.0",
|
|
476
|
+
format: options?.format || inferFormat(envelope),
|
|
477
|
+
payload: envelope.payload,
|
|
478
|
+
signature: {
|
|
479
|
+
alg: envelope.signature.alg || "EdDSA",
|
|
480
|
+
kid: envelope.signature.kid,
|
|
481
|
+
sig: envelope.signature.sig,
|
|
482
|
+
...envelope.signature.pub && { pub: envelope.signature.pub }
|
|
483
|
+
},
|
|
484
|
+
meta: {
|
|
485
|
+
issuer: options?.issuer || "blindllm.com",
|
|
486
|
+
source: "passport",
|
|
487
|
+
timestamp: extractTimestamp(envelope.payload)
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function passportBatchToArtifacts(envelopes, options) {
|
|
492
|
+
return envelopes.map((e) => passportToArtifact(e, options));
|
|
493
|
+
}
|
|
494
|
+
function createPassportAuditBundle(envelopes, keys, options) {
|
|
495
|
+
const artifacts = passportBatchToArtifacts(envelopes, options);
|
|
496
|
+
return {
|
|
497
|
+
version: "1.0",
|
|
498
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
499
|
+
issuer: options?.issuer || "blindllm.com",
|
|
500
|
+
receipts: artifacts,
|
|
501
|
+
keys: Object.fromEntries(
|
|
502
|
+
Object.entries(keys).map(([kid, k]) => [kid, { alg: "EdDSA", pub: k.pub, issuer: k.issuer }])
|
|
503
|
+
),
|
|
504
|
+
summary: {
|
|
505
|
+
total_receipts: artifacts.length,
|
|
506
|
+
time_range: {
|
|
507
|
+
first: Math.min(...artifacts.map((a) => a.meta?.timestamp || 0)),
|
|
508
|
+
last: Math.max(...artifacts.map((a) => a.meta?.timestamp || 0))
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function inferFormat(envelope) {
|
|
514
|
+
const payload = envelope.payload;
|
|
515
|
+
if (payload.type === "arena-battle-receipt") return "passport-battle-v1";
|
|
516
|
+
if (payload.type === "coach-uplift-receipt") return "passport-coach-uplift-v1";
|
|
517
|
+
if (payload.type === "formal-debate-receipt") return "passport-debate-v1";
|
|
518
|
+
if (payload.type === "agent-manifest") return "passport-manifest-v1";
|
|
519
|
+
if (payload.type === "coach-contribution") return "passport-contribution-v1";
|
|
520
|
+
return "passport-generic-v1";
|
|
521
|
+
}
|
|
522
|
+
function extractTimestamp(payload) {
|
|
523
|
+
if (typeof payload === "object" && payload !== null) {
|
|
524
|
+
const p = payload;
|
|
525
|
+
if (typeof p.timestamp === "number") return p.timestamp;
|
|
526
|
+
if (typeof p.created_at === "string") return new Date(p.created_at).getTime();
|
|
527
|
+
if (typeof p.iat === "number") return p.iat * 1e3;
|
|
528
|
+
}
|
|
529
|
+
return Date.now();
|
|
530
|
+
}
|
|
531
|
+
export {
|
|
532
|
+
base58Encode,
|
|
533
|
+
buildPublicKeyEndpoint,
|
|
534
|
+
canonicalHash,
|
|
535
|
+
canonicalize,
|
|
536
|
+
createAgentManifest,
|
|
537
|
+
createArenaBattleReceipt,
|
|
538
|
+
createCoachContribution,
|
|
539
|
+
createCoachManifest,
|
|
540
|
+
createCoachUpliftReceipt,
|
|
541
|
+
createFormalDebateReceipt,
|
|
542
|
+
createOwnershipAttestation,
|
|
543
|
+
createPassportAuditBundle,
|
|
544
|
+
createStatusRecord,
|
|
545
|
+
deriveIssuerFromSeed,
|
|
546
|
+
derivePassportId,
|
|
547
|
+
exportPassportBundle,
|
|
548
|
+
formatKidLabeled,
|
|
549
|
+
formatKidShort,
|
|
550
|
+
generateIssuerKey,
|
|
551
|
+
generatePassportKey,
|
|
552
|
+
getAgentSummary,
|
|
553
|
+
getCoachSummary,
|
|
554
|
+
getSigningKey,
|
|
555
|
+
getVerifyKey,
|
|
556
|
+
hashString,
|
|
557
|
+
importPassportBundle,
|
|
558
|
+
isAgentManifest,
|
|
559
|
+
isCoachManifest,
|
|
560
|
+
passportBatchToArtifacts,
|
|
561
|
+
passportToArtifact,
|
|
562
|
+
serializeBundle,
|
|
563
|
+
signPayload,
|
|
564
|
+
signReceipt,
|
|
565
|
+
verifyEnvelope,
|
|
566
|
+
verifyManifest,
|
|
567
|
+
verifyOwnership
|
|
568
|
+
};
|