@aikdna/kdna-core 0.17.0 → 0.18.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/CHANGELOG.md +131 -2
- package/README.md +145 -2
- package/package.json +3 -2
- package/schema/agent-host-2-receipt.schema.json +206 -0
- package/schema/agent-host-2-request.schema.json +135 -0
- package/schema/agent-host-capabilities-1.schema.json +40 -0
- package/schema/checksums.schema.json +40 -1
- package/schema/consumption-plan-1.schema.json +280 -0
- package/schema/digest-evidence.schema.json +123 -0
- package/schema/judgment-trace-1.schema.json +748 -0
- package/schema/load-plan.schema.json +2 -2
- package/schema/runtime-capsule-1.schema.json +68 -0
- package/schema/runtime-capsule-2.schema.json +140 -0
- package/src/asset-reader.js +100 -120
- package/src/capsule-v2.js +687 -0
- package/src/container-dispatcher.js +4 -1
- package/src/execution-contract-v1.js +1850 -0
- package/src/index.js +4 -0
- package/src/index.mjs +136 -21
- package/src/lint-pure.js +6 -3
- package/src/public-api.js +59 -163
- package/src/runtime-api.js +5 -6
- package/src/types.d.ts +1181 -113
- package/src/v1/index.js +225 -55
- package/src/v1/index.mjs +1 -0
|
@@ -126,8 +126,8 @@
|
|
|
126
126
|
"required": ["kind", "path"],
|
|
127
127
|
"additionalProperties": false,
|
|
128
128
|
"properties": {
|
|
129
|
-
"kind": { "type": ["string", "null"], "enum": ["dir", "file", null] },
|
|
130
|
-
"path": { "type": "string" }
|
|
129
|
+
"kind": { "type": ["string", "null"], "enum": ["dir", "file", "memory", null] },
|
|
130
|
+
"path": { "type": ["string", "null"] }
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/aikdna/kdna/specs/runtime-capsule-1.schema.json",
|
|
4
|
+
"title": "KDNA Runtime Capsule 1",
|
|
5
|
+
"description": "Frozen compatibility schema. The historical asset_digest member means E (kdna-runtime-entry-set-v1), never the final container digest A.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"type",
|
|
9
|
+
"version",
|
|
10
|
+
"domain",
|
|
11
|
+
"judgment_version",
|
|
12
|
+
"asset_digest",
|
|
13
|
+
"signature",
|
|
14
|
+
"access",
|
|
15
|
+
"risk_level",
|
|
16
|
+
"profile",
|
|
17
|
+
"context",
|
|
18
|
+
"trace"
|
|
19
|
+
],
|
|
20
|
+
"additionalProperties": true,
|
|
21
|
+
"properties": {
|
|
22
|
+
"type": { "const": "kdna.context.capsule" },
|
|
23
|
+
"version": { "const": "1.0" },
|
|
24
|
+
"domain": { "type": ["string", "null"] },
|
|
25
|
+
"judgment_version": { "type": ["string", "null"] },
|
|
26
|
+
"asset_digest": {
|
|
27
|
+
"description": "Frozen Capsule 1 name for the runtime entry-set digest E.",
|
|
28
|
+
"oneOf": [{ "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }, { "type": "null" }]
|
|
29
|
+
},
|
|
30
|
+
"signature": { "$ref": "#/$defs/signature" },
|
|
31
|
+
"access": { "type": "string" },
|
|
32
|
+
"risk_level": { "type": ["string", "null"] },
|
|
33
|
+
"profile": { "enum": ["index", "compact", "scenario", "full"] },
|
|
34
|
+
"context": { "type": "object" },
|
|
35
|
+
"trace": { "$ref": "#/$defs/trace" }
|
|
36
|
+
},
|
|
37
|
+
"$defs": {
|
|
38
|
+
"signature": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": ["state"],
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"properties": {
|
|
43
|
+
"state": { "enum": ["verified", "not_checked", "absent"] },
|
|
44
|
+
"issuer": { "type": "string", "minLength": 1 }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"trace": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"required": [
|
|
50
|
+
"payload_encoding",
|
|
51
|
+
"loaded_by",
|
|
52
|
+
"loaded_at",
|
|
53
|
+
"schema_valid",
|
|
54
|
+
"signature_state",
|
|
55
|
+
"profile"
|
|
56
|
+
],
|
|
57
|
+
"additionalProperties": true,
|
|
58
|
+
"properties": {
|
|
59
|
+
"payload_encoding": { "const": "cbor" },
|
|
60
|
+
"loaded_by": { "const": "kdna-core" },
|
|
61
|
+
"loaded_at": { "type": "string", "format": "date-time" },
|
|
62
|
+
"schema_valid": { "type": "boolean" },
|
|
63
|
+
"signature_state": { "enum": ["verified", "not_checked", "absent"] },
|
|
64
|
+
"profile": { "enum": ["index", "compact", "scenario", "full"] }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/aikdna/kdna/specs/runtime-capsule-2.schema.json",
|
|
4
|
+
"title": "KDNA Runtime Capsule 2",
|
|
5
|
+
"description": "Opt-in Runtime Capsule carrying explicit A/C/E digest evidence. P is external delivery metadata and is never embedded here.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"type",
|
|
9
|
+
"version",
|
|
10
|
+
"asset",
|
|
11
|
+
"digests",
|
|
12
|
+
"signature",
|
|
13
|
+
"access",
|
|
14
|
+
"risk_level",
|
|
15
|
+
"profile",
|
|
16
|
+
"context",
|
|
17
|
+
"trace"
|
|
18
|
+
],
|
|
19
|
+
"additionalProperties": false,
|
|
20
|
+
"properties": {
|
|
21
|
+
"type": { "const": "kdna.context.capsule" },
|
|
22
|
+
"version": { "const": "2.0" },
|
|
23
|
+
"asset": { "$ref": "#/$defs/asset" },
|
|
24
|
+
"digests": {
|
|
25
|
+
"allOf": [
|
|
26
|
+
{
|
|
27
|
+
"$ref": "https://github.com/aikdna/kdna/specs/digest-evidence.schema.json"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"properties": {
|
|
31
|
+
"asset": { "$ref": "#/$defs/successfulDigest" },
|
|
32
|
+
"content": { "$ref": "#/$defs/successfulDigest" },
|
|
33
|
+
"runtime_entry_set": { "$ref": "#/$defs/successfulDigest" }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"signature": { "$ref": "#/$defs/signature" },
|
|
39
|
+
"access": { "enum": ["public", "licensed", "remote"] },
|
|
40
|
+
"risk_level": { "type": ["string", "null"] },
|
|
41
|
+
"profile": { "enum": ["index", "compact", "scenario", "full"] },
|
|
42
|
+
"context": { "type": "object" },
|
|
43
|
+
"trace": { "$ref": "#/$defs/trace" },
|
|
44
|
+
"compatibility": { "$ref": "#/$defs/compatibility" }
|
|
45
|
+
},
|
|
46
|
+
"$defs": {
|
|
47
|
+
"sha256": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
50
|
+
},
|
|
51
|
+
"asset": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["asset_id", "asset_uid", "version", "judgment_version"],
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"asset_id": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"pattern": "^[a-zA-Z][a-zA-Z0-9_-]*(:[a-zA-Z0-9_.-]+)+$"
|
|
59
|
+
},
|
|
60
|
+
"asset_uid": { "type": "string", "format": "uri" },
|
|
61
|
+
"version": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+([+-].+)?$"
|
|
64
|
+
},
|
|
65
|
+
"judgment_version": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+([+-].+)?$"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"successfulDigest": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"properties": {
|
|
74
|
+
"value": { "$ref": "#/$defs/sha256" },
|
|
75
|
+
"comparison": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"properties": {
|
|
78
|
+
"state": { "enum": ["matched", "not_compared"] }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"signature": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"required": ["state"],
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"properties": {
|
|
88
|
+
"state": { "enum": ["verified", "not_checked", "absent"] },
|
|
89
|
+
"issuer": { "type": "string", "minLength": 1 }
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"trace": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"required": [
|
|
95
|
+
"payload_encoding",
|
|
96
|
+
"loaded_by",
|
|
97
|
+
"loaded_at",
|
|
98
|
+
"input_kind",
|
|
99
|
+
"runtime_eligible",
|
|
100
|
+
"schema_valid",
|
|
101
|
+
"signature_state",
|
|
102
|
+
"profile"
|
|
103
|
+
],
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"properties": {
|
|
106
|
+
"payload_encoding": { "const": "cbor" },
|
|
107
|
+
"loaded_by": { "const": "kdna-core" },
|
|
108
|
+
"loaded_at": { "type": "string", "format": "date-time" },
|
|
109
|
+
"input_kind": { "enum": ["packaged_file", "packaged_bytes"] },
|
|
110
|
+
"runtime_eligible": { "const": true },
|
|
111
|
+
"schema_valid": { "const": true },
|
|
112
|
+
"signature_state": { "enum": ["verified", "not_checked", "absent"] },
|
|
113
|
+
"profile": { "enum": ["index", "compact", "scenario", "full"] }
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"compatibility": {
|
|
117
|
+
"description": "Adapter-only values with no Capsule 2 identity or routing authority.",
|
|
118
|
+
"type": "object",
|
|
119
|
+
"minProperties": 1,
|
|
120
|
+
"additionalProperties": false,
|
|
121
|
+
"properties": {
|
|
122
|
+
"capsule_1_domain": { "type": "string", "minLength": 1 },
|
|
123
|
+
"capsule_1_access": { "enum": ["open", "protected", "runtime"] },
|
|
124
|
+
"capsule_1_extensions": { "$ref": "#/$defs/capsule1Extensions" }
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"capsule1Extensions": {
|
|
128
|
+
"description": "Frozen Capsule 1 extension values carried only for exact one-way adapter parity.",
|
|
129
|
+
"type": "object",
|
|
130
|
+
"minProperties": 1,
|
|
131
|
+
"additionalProperties": false,
|
|
132
|
+
"properties": {
|
|
133
|
+
"extends_chain": { "type": "array" },
|
|
134
|
+
"inheritance_applied": { "type": "boolean" },
|
|
135
|
+
"resolved_dependencies": { "type": "array" },
|
|
136
|
+
"rag_isolation_policy": { "type": "object" }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
package/src/asset-reader.js
CHANGED
|
@@ -6,15 +6,13 @@ const fs = require('fs');
|
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const zlib = require('zlib');
|
|
8
8
|
const cbor = require('cbor-x');
|
|
9
|
-
const { loadDomainFromFiles, formatContext } = require('./loader');
|
|
10
9
|
|
|
11
10
|
const KDNA_MEDIA_TYPE = 'application/vnd.kdna.asset';
|
|
11
|
+
const ASSET_SOURCE_BYTES = Symbol.for('@aikdna/kdna-core.asset-source-bytes');
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// add them here. KDNA_Cluster is intentionally omitted — its schema exists but
|
|
17
|
-
// is not yet wired into the loader contract.
|
|
13
|
+
// Deprecated legacy authoring source entry names. Current Runtime assets carry
|
|
14
|
+
// judgment only in payload.kdnab and never expose these as distribution entries.
|
|
15
|
+
// Kept for source-migration callers; loadProfile does not use this list.
|
|
18
16
|
const STANDARD_ENTRIES = Object.freeze([
|
|
19
17
|
'KDNA_Core.json',
|
|
20
18
|
'KDNA_Patterns.json',
|
|
@@ -43,6 +41,14 @@ function stableStringify(value) {
|
|
|
43
41
|
return JSON.stringify(value);
|
|
44
42
|
}
|
|
45
43
|
|
|
44
|
+
function compareEntryNamesByUtf8(left, right) {
|
|
45
|
+
return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function sortedEntryNames(entries) {
|
|
49
|
+
return [...entries.keys()].sort(compareEntryNamesByUtf8);
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
function parseJson(buf, entryName) {
|
|
47
53
|
try {
|
|
48
54
|
return JSON.parse(Buffer.isBuffer(buf) ? buf.toString('utf8') : String(buf));
|
|
@@ -51,6 +57,17 @@ function parseJson(buf, entryName) {
|
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
|
|
60
|
+
function validateDecodedEntry(buf, entryName) {
|
|
61
|
+
if (entryName === 'payload.kdnab') {
|
|
62
|
+
try {
|
|
63
|
+
return cbor.decode(buf);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
throw new Error(`${entryName}: invalid CBOR: ${e.message}`, { cause: e });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return parseJson(buf, entryName);
|
|
69
|
+
}
|
|
70
|
+
|
|
54
71
|
function encryptedEntries(manifest) {
|
|
55
72
|
const entries = manifest?.encryption?.encrypted_entries;
|
|
56
73
|
return Array.isArray(entries) ? entries : [];
|
|
@@ -188,7 +205,7 @@ function manifestForDigest(manifest) {
|
|
|
188
205
|
function buildContentDigest(asset) {
|
|
189
206
|
const parts = [];
|
|
190
207
|
const excluded = new Set(['.DS_Store', 'signature.json', 'build-receipt.json']);
|
|
191
|
-
for (const entryName of
|
|
208
|
+
for (const entryName of sortedEntryNames(asset.entries)) {
|
|
192
209
|
if (excluded.has(entryName)) continue;
|
|
193
210
|
if (entryName.startsWith('reports/')) continue;
|
|
194
211
|
const entryBuf = asset.readEntry(entryName);
|
|
@@ -243,7 +260,7 @@ function buildSigningPayload(asset, options = {}) {
|
|
|
243
260
|
// byte strings and the signature would fail to verify. The fix
|
|
244
261
|
// mirrors `buildContentDigest`'s exclusion set exactly.
|
|
245
262
|
const parts = [];
|
|
246
|
-
for (const entryName of
|
|
263
|
+
for (const entryName of sortedEntryNames(asset.entries)) {
|
|
247
264
|
if (entryName === '.DS_Store' || entryName === 'signature.json') continue;
|
|
248
265
|
if (entryName === 'build-receipt.json') continue;
|
|
249
266
|
if (entryName.startsWith('reports/')) continue;
|
|
@@ -324,7 +341,9 @@ function verifyHumanLockSignatures(coreData, manifest, errors, warnings) {
|
|
|
324
341
|
['stances', 'stance'],
|
|
325
342
|
];
|
|
326
343
|
|
|
327
|
-
let verified = 0,
|
|
344
|
+
let verified = 0,
|
|
345
|
+
missing = 0,
|
|
346
|
+
invalid = 0;
|
|
328
347
|
for (const [arrayName] of CORE_CARD_ARRAYS) {
|
|
329
348
|
const cards = coreData?.[arrayName];
|
|
330
349
|
if (!Array.isArray(cards) || cards.length === 0) continue;
|
|
@@ -342,8 +361,11 @@ function verifyHumanLockSignatures(coreData, manifest, errors, warnings) {
|
|
|
342
361
|
const sig = Buffer.from(String(hl.signature).replace(/^ed25519:/, ''), 'hex');
|
|
343
362
|
const key = crypto.createPublicKey(publicKeyPEM);
|
|
344
363
|
const ok = crypto.verify(null, Buffer.from(payload), key, sig);
|
|
345
|
-
if (ok) {
|
|
346
|
-
|
|
364
|
+
if (ok) {
|
|
365
|
+
verified++;
|
|
366
|
+
} else {
|
|
367
|
+
invalid++;
|
|
368
|
+
}
|
|
347
369
|
} catch {
|
|
348
370
|
invalid++;
|
|
349
371
|
}
|
|
@@ -351,7 +373,9 @@ function verifyHumanLockSignatures(coreData, manifest, errors, warnings) {
|
|
|
351
373
|
}
|
|
352
374
|
|
|
353
375
|
if (invalid > 0) {
|
|
354
|
-
errors.push(
|
|
376
|
+
errors.push(
|
|
377
|
+
`${invalid} Human Lock signature(s) failed verification — judgment may have been altered`,
|
|
378
|
+
);
|
|
355
379
|
}
|
|
356
380
|
if (missing > 0) {
|
|
357
381
|
warnings.push(`${missing} locked card(s) have no Human Lock signature`);
|
|
@@ -361,29 +385,10 @@ function verifyHumanLockSignatures(coreData, manifest, errors, warnings) {
|
|
|
361
385
|
}
|
|
362
386
|
}
|
|
363
387
|
|
|
364
|
-
function validateManifestIdentity(manifest, errors, _warnings) {
|
|
365
|
-
if (manifest.kdna_spec) {
|
|
366
|
-
errors.push('kdna.json: kdna_spec is not allowed. Use kdna_version.');
|
|
367
|
-
}
|
|
368
|
-
if (manifest.format && manifest.format !== 'kdna') {
|
|
369
|
-
errors.push(`kdna.json.format: invalid value "${manifest.format}". Expected "kdna".`);
|
|
370
|
-
}
|
|
371
|
-
if (!manifest.kdna_version) {
|
|
372
|
-
errors.push('kdna.json: missing required field "kdna_version"');
|
|
373
|
-
} else if (manifest.kdna_version !== '1.0') {
|
|
374
|
-
errors.push(
|
|
375
|
-
`kdna.json.kdna_version: invalid value "${manifest.kdna_version}". Expected "1.0".`,
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
if (manifest.language) {
|
|
379
|
-
errors.push('kdna.json: language is not allowed. Use default_language and languages.');
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
388
|
function openAsset(input) {
|
|
384
389
|
const { buffer, path } = normalizeInput(input);
|
|
385
390
|
const entries = parseZipEntries(buffer);
|
|
386
|
-
|
|
391
|
+
const asset = {
|
|
387
392
|
path,
|
|
388
393
|
size: buffer.length,
|
|
389
394
|
asset_digest: `sha256:${sha256Hex(buffer)}`,
|
|
@@ -394,6 +399,29 @@ function openAsset(input) {
|
|
|
394
399
|
return readZipEntry(buffer, entry);
|
|
395
400
|
},
|
|
396
401
|
};
|
|
402
|
+
Object.defineProperty(asset, ASSET_SOURCE_BYTES, {
|
|
403
|
+
value: buffer,
|
|
404
|
+
enumerable: false,
|
|
405
|
+
writable: false,
|
|
406
|
+
});
|
|
407
|
+
return asset;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function validateCurrentContainer(asset) {
|
|
411
|
+
const bytes = asset[ASSET_SOURCE_BYTES];
|
|
412
|
+
if (!Buffer.isBuffer(bytes)) {
|
|
413
|
+
throw new Error('Current KDNA validation requires the original .kdna container bytes');
|
|
414
|
+
}
|
|
415
|
+
return require('./v1').validate(bytes);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function appendCurrentValidationErrors(asset, errors) {
|
|
419
|
+
try {
|
|
420
|
+
const validation = validateCurrentContainer(asset);
|
|
421
|
+
if (!validation.overall_valid) errors.push(...validation.problems);
|
|
422
|
+
} catch (e) {
|
|
423
|
+
errors.push(e.message);
|
|
424
|
+
}
|
|
397
425
|
}
|
|
398
426
|
|
|
399
427
|
function listEntries(asset) {
|
|
@@ -409,30 +437,18 @@ function readManifest(asset) {
|
|
|
409
437
|
return parseJson(asset.readEntry('kdna.json'), 'kdna.json');
|
|
410
438
|
}
|
|
411
439
|
|
|
412
|
-
function readDataMapSync(asset,
|
|
413
|
-
const dataMap = {};
|
|
414
|
-
const manifest = readManifest(asset);
|
|
415
|
-
|
|
440
|
+
function readDataMapSync(asset, _options = {}) {
|
|
416
441
|
if (asset.entries.has('payload.kdnab')) {
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (payload.judgment.scenarios) dataMap['KDNA_Scenarios.json'] = payload.judgment.scenarios;
|
|
423
|
-
if (payload.judgment.cases) dataMap['KDNA_Cases.json'] = payload.judgment.cases;
|
|
424
|
-
if (payload.judgment.reasoning) dataMap['KDNA_Reasoning.json'] = payload.judgment.reasoning;
|
|
425
|
-
if (payload.judgment.evolution) dataMap['KDNA_Evolution.json'] = payload.judgment.evolution;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const encrypted = encryptedEntries(manifest);
|
|
429
|
-
if (encrypted.length && typeof options.decryptEntry !== 'function') {
|
|
430
|
-
throw new Error(`encrypted entries require decryptEntry hook: ${encrypted.join(', ')}`);
|
|
431
|
-
}
|
|
432
|
-
return dataMap;
|
|
442
|
+
const error = new Error(
|
|
443
|
+
'readDataMap is a legacy source-tree API and does not project current payload.kdnab into KDNA_Core.json/KDNA_Patterns.json. Use loadProfile or loadAuthorized.',
|
|
444
|
+
);
|
|
445
|
+
error.code = 'KDNA_LEGACY_DATA_MAP_UNSUPPORTED';
|
|
446
|
+
throw error;
|
|
433
447
|
}
|
|
434
448
|
|
|
435
|
-
|
|
449
|
+
const error = new Error('Not a current KDNA asset: missing payload.kdnab');
|
|
450
|
+
error.code = 'KDNA_FORMAT_INVALID';
|
|
451
|
+
throw error;
|
|
436
452
|
}
|
|
437
453
|
|
|
438
454
|
function verifySync(asset, options = {}) {
|
|
@@ -440,6 +456,12 @@ function verifySync(asset, options = {}) {
|
|
|
440
456
|
const warnings = [];
|
|
441
457
|
const entries = listEntries(asset);
|
|
442
458
|
|
|
459
|
+
// The current v1 implementation is the sole authority for container,
|
|
460
|
+
// manifest, CBOR payload, checksum, and load-contract validity. The reader
|
|
461
|
+
// adds transport identity and legacy signature hooks; it must not maintain a
|
|
462
|
+
// second manifest or payload dialect.
|
|
463
|
+
appendCurrentValidationErrors(asset, errors);
|
|
464
|
+
|
|
443
465
|
if (!asset.entries.has('kdna.json')) errors.push('required entry missing: kdna.json');
|
|
444
466
|
verifyMediaType(asset, errors);
|
|
445
467
|
if (!asset.entries.has('payload.kdnab')) errors.push('required entry missing: payload.kdnab');
|
|
@@ -450,7 +472,9 @@ function verifySync(asset, options = {}) {
|
|
|
450
472
|
errors.push(`asset digest mismatch: expected ${options.asset_digest}, got ${asset_digest}`);
|
|
451
473
|
}
|
|
452
474
|
if (options.content_digest && options.content_digest !== content_digest) {
|
|
453
|
-
errors.push(
|
|
475
|
+
errors.push(
|
|
476
|
+
`content digest mismatch: expected ${options.content_digest}, got ${content_digest}`,
|
|
477
|
+
);
|
|
454
478
|
}
|
|
455
479
|
|
|
456
480
|
let manifest = null;
|
|
@@ -458,7 +482,6 @@ function verifySync(asset, options = {}) {
|
|
|
458
482
|
if (asset.entries.has('kdna.json')) {
|
|
459
483
|
try {
|
|
460
484
|
manifest = readManifest(asset);
|
|
461
|
-
validateManifestIdentity(manifest, errors, warnings);
|
|
462
485
|
const encrypted = encryptedEntries(manifest);
|
|
463
486
|
if (encrypted.length) {
|
|
464
487
|
warnings.push(`encrypted entries present: ${encrypted.join(', ')}`);
|
|
@@ -479,7 +502,7 @@ function verifySync(asset, options = {}) {
|
|
|
479
502
|
asset.readEntry(entryName),
|
|
480
503
|
options,
|
|
481
504
|
);
|
|
482
|
-
|
|
505
|
+
validateDecodedEntry(decrypted, entryName);
|
|
483
506
|
} catch (e) {
|
|
484
507
|
errors.push(e.message);
|
|
485
508
|
}
|
|
@@ -516,29 +539,15 @@ function verifySync(asset, options = {}) {
|
|
|
516
539
|
}
|
|
517
540
|
|
|
518
541
|
function loadProfileSync(asset, profile = 'compact', options = {}) {
|
|
519
|
-
const
|
|
520
|
-
if (
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
content_digest: buildContentDigest(asset),
|
|
526
|
-
entries: listEntries(asset),
|
|
527
|
-
name: manifest.name || manifest.asset_id || null,
|
|
528
|
-
version: manifest.version || null,
|
|
529
|
-
judgment_version: manifest.judgment_version || null,
|
|
530
|
-
keywords: manifest.keywords || [],
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
const dataMap = readDataMapSync(asset, STANDARD_ENTRIES, options);
|
|
534
|
-
const mode = profile === 'full' ? 'all' : profile === 'scenario' ? 'auto' : 'minimum';
|
|
535
|
-
const domain = loadDomainFromFiles(dataMap, { mode, input: options.input || '' });
|
|
536
|
-
return {
|
|
542
|
+
const bytes = asset[ASSET_SOURCE_BYTES];
|
|
543
|
+
if (!Buffer.isBuffer(bytes)) {
|
|
544
|
+
throw new Error('loadProfile requires the original .kdna container bytes');
|
|
545
|
+
}
|
|
546
|
+
return require('./runtime-api').loadAuthorized(bytes, {
|
|
547
|
+
...options,
|
|
537
548
|
profile,
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
context: options.context === false || !domain ? null : formatContext(domain),
|
|
541
|
-
};
|
|
549
|
+
as: options.as || 'json',
|
|
550
|
+
});
|
|
542
551
|
}
|
|
543
552
|
|
|
544
553
|
function createKdnaAssetReader() {
|
|
@@ -565,7 +574,13 @@ function createKdnaAssetReader() {
|
|
|
565
574
|
if (!asset.entries.has(entryName)) return null;
|
|
566
575
|
const manifest =
|
|
567
576
|
entryName === 'kdna.json' ? null : parseJson(asset.readEntry('kdna.json'), 'kdna.json');
|
|
568
|
-
const buf = maybeDecryptEntrySync(
|
|
577
|
+
const buf = maybeDecryptEntrySync(
|
|
578
|
+
asset,
|
|
579
|
+
manifest,
|
|
580
|
+
entryName,
|
|
581
|
+
asset.readEntry(entryName),
|
|
582
|
+
options,
|
|
583
|
+
);
|
|
569
584
|
return parseJson(buf, entryName);
|
|
570
585
|
},
|
|
571
586
|
|
|
@@ -591,21 +606,8 @@ function createKdnaAssetReader() {
|
|
|
591
606
|
|
|
592
607
|
readDataMapSync,
|
|
593
608
|
|
|
594
|
-
async readDataMap(asset,
|
|
595
|
-
|
|
596
|
-
const manifest = await this.readManifest(asset);
|
|
597
|
-
for (const entryName of entries) {
|
|
598
|
-
if (!asset.entries.has(entryName)) continue;
|
|
599
|
-
const buf = await maybeDecryptEntry(
|
|
600
|
-
asset,
|
|
601
|
-
manifest,
|
|
602
|
-
entryName,
|
|
603
|
-
asset.readEntry(entryName),
|
|
604
|
-
options,
|
|
605
|
-
);
|
|
606
|
-
dataMap[entryName] = parseJson(buf, entryName);
|
|
607
|
-
}
|
|
608
|
-
return dataMap;
|
|
609
|
+
async readDataMap(asset, _entries = STANDARD_ENTRIES, options = {}) {
|
|
610
|
+
return readDataMapSync(asset, options);
|
|
609
611
|
},
|
|
610
612
|
|
|
611
613
|
contentDigestSync: buildContentDigest,
|
|
@@ -621,6 +623,8 @@ function createKdnaAssetReader() {
|
|
|
621
623
|
const warnings = [];
|
|
622
624
|
const entries = [...asset.entries.keys()].sort();
|
|
623
625
|
|
|
626
|
+
appendCurrentValidationErrors(asset, errors);
|
|
627
|
+
|
|
624
628
|
if (!asset.entries.has('kdna.json')) errors.push('required entry missing: kdna.json');
|
|
625
629
|
verifyMediaType(asset, errors);
|
|
626
630
|
if (!asset.entries.has('payload.kdnab')) errors.push('required entry missing: payload.kdnab');
|
|
@@ -641,7 +645,6 @@ function createKdnaAssetReader() {
|
|
|
641
645
|
if (asset.entries.has('kdna.json')) {
|
|
642
646
|
try {
|
|
643
647
|
manifest = parseJson(asset.readEntry('kdna.json'), 'kdna.json');
|
|
644
|
-
validateManifestIdentity(manifest, errors, warnings);
|
|
645
648
|
const encrypted = encryptedEntries(manifest);
|
|
646
649
|
if (encrypted.length) {
|
|
647
650
|
warnings.push(`encrypted entries present: ${encrypted.join(', ')}`);
|
|
@@ -662,7 +665,7 @@ function createKdnaAssetReader() {
|
|
|
662
665
|
asset.readEntry(entryName),
|
|
663
666
|
options,
|
|
664
667
|
);
|
|
665
|
-
|
|
668
|
+
validateDecodedEntry(decrypted, entryName);
|
|
666
669
|
} catch (e) {
|
|
667
670
|
errors.push(e.message);
|
|
668
671
|
}
|
|
@@ -692,30 +695,7 @@ function createKdnaAssetReader() {
|
|
|
692
695
|
loadProfileSync,
|
|
693
696
|
|
|
694
697
|
async loadProfile(asset, profile = 'compact', options = {}) {
|
|
695
|
-
|
|
696
|
-
if (profile === 'index') {
|
|
697
|
-
return {
|
|
698
|
-
profile,
|
|
699
|
-
manifest,
|
|
700
|
-
asset_digest: asset.asset_digest,
|
|
701
|
-
content_digest: buildContentDigest(asset),
|
|
702
|
-
entries: await this.listEntries(asset),
|
|
703
|
-
name: manifest.name || manifest.asset_id || null,
|
|
704
|
-
version: manifest.version || null,
|
|
705
|
-
judgment_version: manifest.judgment_version || null,
|
|
706
|
-
keywords: manifest.keywords || [],
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
const dataMap = await this.readDataMap(asset, STANDARD_ENTRIES, options);
|
|
711
|
-
const mode = profile === 'full' ? 'all' : profile === 'scenario' ? 'auto' : 'minimum';
|
|
712
|
-
const domain = loadDomainFromFiles(dataMap, { mode, input: options.input || '' });
|
|
713
|
-
return {
|
|
714
|
-
profile,
|
|
715
|
-
manifest,
|
|
716
|
-
domain,
|
|
717
|
-
context: options.context === false || !domain ? null : formatContext(domain),
|
|
718
|
-
};
|
|
698
|
+
return loadProfileSync(asset, profile, options);
|
|
719
699
|
},
|
|
720
700
|
};
|
|
721
701
|
}
|