@aikdna/kdna-core 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +136 -94
  2. package/README.md +109 -215
  3. package/package.json +7 -2
  4. package/schema/agent-host-capabilities.schema.json +62 -0
  5. package/schema/agent-host-receipt.schema.json +371 -0
  6. package/schema/agent-host-request.schema.json +223 -0
  7. package/schema/{bundle-profile-v1.schema.json → bundle-profile.schema.json} +9 -5
  8. package/schema/checksums.schema.json +8 -8
  9. package/schema/consumption-plan.schema.json +428 -0
  10. package/schema/digest-evidence.schema.json +115 -26
  11. package/schema/{kdna-envelope-external-grant-v1.schema.json → external-grant-envelope.schema.json} +17 -4
  12. package/schema/{kdna-key-grant-v1.schema.json → external-key-grant.schema.json} +39 -7
  13. package/schema/judgment-trace.schema.json +1251 -0
  14. package/schema/load-plan.schema.json +5 -3
  15. package/schema/manifest.schema.json +78 -10
  16. package/schema/{payload-profile-v1.schema.json → payload-profile.schema.json} +30 -6
  17. package/schema/{runtime-capsule-2.schema.json → runtime-capsule.schema.json} +7 -33
  18. package/src/asset-reader.js +8 -2
  19. package/src/{v1 → container}/index.js +385 -198
  20. package/src/container/index.mjs +20 -0
  21. package/src/container-dispatcher.js +6 -6
  22. package/src/crypto-profile.js +37 -107
  23. package/src/external-key-grant.js +41 -15
  24. package/src/index.js +11 -8
  25. package/src/index.mjs +49 -45
  26. package/src/loader-compatibility.js +59 -0
  27. package/src/loader.js +1 -1
  28. package/src/public-api.js +5 -5
  29. package/src/remote-runtime.d.ts +13 -0
  30. package/src/remote-runtime.js +27 -0
  31. package/src/remote-runtime.mjs +3 -0
  32. package/src/runtime-api.js +5 -5
  33. package/src/{capsule-v2.js → runtime-capsule.js} +95 -254
  34. package/src/{execution-contract-v1.js → runtime-contract.js} +184 -152
  35. package/src/types.d.ts +200 -278
  36. package/src/workpack-pure.js +1 -1
  37. package/schema/agent-host-2-receipt.schema.json +0 -206
  38. package/schema/agent-host-2-request.schema.json +0 -135
  39. package/schema/agent-host-capabilities-1.schema.json +0 -40
  40. package/schema/consumption-plan-1.schema.json +0 -280
  41. package/schema/judgment-trace-1.schema.json +0 -748
  42. package/schema/runtime-capsule-1.schema.json +0 -68
  43. package/src/lint-pure.js +0 -433
  44. package/src/v1/index.mjs +0 -21
@@ -3,18 +3,20 @@
3
3
  const crypto = require('node:crypto');
4
4
  const fs = require('node:fs');
5
5
 
6
- const Ajv2020 = require('ajv/dist/2020');
6
+ const JsonSchema2020 = require('ajv/dist/2020');
7
7
  const addFormats = require('ajv-formats');
8
8
 
9
9
  const { createKdnaAssetReader } = require('./asset-reader');
10
- const v1 = require('./v1');
10
+ const container = require('./container');
11
11
  const digestEvidenceSchema = require('../schema/digest-evidence.schema.json');
12
- const capsule1Schema = require('../schema/runtime-capsule-1.schema.json');
13
- const capsule2Schema = require('../schema/runtime-capsule-2.schema.json');
12
+ const runtimeCapsuleSchema = require('../schema/runtime-capsule.schema.json');
14
13
 
15
14
  const SHA256_RE = /^sha256:[0-9a-f]{64}$/;
16
- const DIGEST_PROFILE = 'kdna-capsule-digests-v1';
17
- const CAPSULE_DIGEST_PROFILE = 'kdna-capsule-jcs-v1';
15
+ const DIGEST_PROFILE = 'kdna.digest-evidence';
16
+ const DIGEST_PROFILE_VERSION = '0.1.0';
17
+ const CAPSULE_DIGEST_PROFILE = 'kdna.canonicalization.runtime-capsule-jcs';
18
+ const CANONICALIZATION_PROFILE_VERSION = '0.1.0';
19
+ const RUNTIME_CAPSULE_CONTRACT_VERSION = '0.1.0';
18
20
  const COMPARISON_SOURCES = new Set([
19
21
  'caller',
20
22
  'registry',
@@ -23,7 +25,6 @@ const COMPARISON_SOURCES = new Set([
23
25
  'kdna.json.content_digest',
24
26
  'kdna.json.authoring.content_digest',
25
27
  'checksums.json.entry_set_digest',
26
- 'checksums.json.asset_digest',
27
28
  ]);
28
29
  const EXTERNAL_COMPARISON_SOURCES = new Set([
29
30
  'caller',
@@ -37,32 +38,19 @@ const COMPARISON_TARGETS = new Set([
37
38
  'checksum_declaration',
38
39
  ]);
39
40
  const BASIS = Object.freeze({
40
- asset: 'kdna-container-bytes-v1',
41
- content: 'kdna-content-tree-v1',
42
- runtime_entry_set: 'kdna-runtime-entry-set-v1',
41
+ asset: 'kdna.digest-basis.container-bytes',
42
+ content: 'kdna.digest-basis.content-tree',
43
+ runtime_entry_set: 'kdna.digest-basis.runtime-entry-set',
43
44
  });
44
- const CAPSULE_1_EXTENSION_KEYS = Object.freeze([
45
- 'extends_chain',
46
- 'inheritance_applied',
47
- 'resolved_dependencies',
48
- 'rag_isolation_policy',
49
- ]);
50
- const CAPSULE_1_ACCESS_MAP = Object.freeze({
51
- open: 'public',
52
- protected: 'licensed',
53
- runtime: 'remote',
54
- });
55
-
56
45
  let validators;
57
46
 
58
47
  function schemaValidators() {
59
48
  if (validators) return validators;
60
- const ajv = new Ajv2020({ allErrors: true, strict: false });
49
+ const ajv = new JsonSchema2020({ allErrors: true, strict: false });
61
50
  addFormats(ajv);
62
51
  ajv.addSchema(digestEvidenceSchema);
63
52
  validators = {
64
- capsule1: ajv.compile(capsule1Schema),
65
- capsule2: ajv.compile(capsule2Schema),
53
+ runtimeCapsule: ajv.compile(runtimeCapsuleSchema),
66
54
  };
67
55
  return validators;
68
56
  }
@@ -85,8 +73,8 @@ function snapshotPackagedBytes(input) {
85
73
  }
86
74
  if (typeof input !== 'string') {
87
75
  fail(
88
- 'KDNA_CAPSULE_2_INPUT_INVALID',
89
- 'Capsule 2 requires a packaged .kdna path, Buffer, or Uint8Array.',
76
+ 'KDNA_RUNTIME_CAPSULE_INPUT_INVALID',
77
+ 'Runtime Capsule requires a packaged .kdna path, Buffer, or Uint8Array.',
90
78
  );
91
79
  }
92
80
 
@@ -94,16 +82,16 @@ function snapshotPackagedBytes(input) {
94
82
  try {
95
83
  stat = fs.statSync(input);
96
84
  } catch (error) {
97
- fail('KDNA_CAPSULE_2_INPUT_INVALID', `Cannot read packaged KDNA asset: ${error.message}`);
85
+ fail('KDNA_RUNTIME_CAPSULE_INPUT_INVALID', `Cannot read packaged KDNA asset: ${error.message}`);
98
86
  }
99
87
  if (stat.isDirectory()) {
100
88
  fail(
101
89
  'KDNA_ASSET_FILE_REQUIRED',
102
- 'Runtime Capsule 2 requires final packaged .kdna bytes; authoring directories are not runtime assets.',
90
+ 'Runtime Capsule requires final packaged .kdna bytes; authoring directories are not runtime assets.',
103
91
  );
104
92
  }
105
93
  if (!stat.isFile()) {
106
- fail('KDNA_CAPSULE_2_INPUT_INVALID', 'Capsule 2 input is not a regular file.');
94
+ fail('KDNA_RUNTIME_CAPSULE_INPUT_INVALID', 'Runtime Capsule input is not a regular file.');
107
95
  }
108
96
  return { bytes: fs.readFileSync(input), inputKind: 'packaged_file' };
109
97
  }
@@ -189,23 +177,12 @@ function declaredContentDigest(manifest) {
189
177
 
190
178
  function declaredEntrySetDigest(checksums) {
191
179
  if (!checksums) return null;
192
- const canonical = checksums.entry_set_digest !== undefined
180
+ return checksums.entry_set_digest !== undefined
193
181
  ? normalizeDeclaredDigest(
194
182
  checksums.entry_set_digest,
195
183
  'checksums.json.entry_set_digest',
196
184
  )
197
185
  : null;
198
- const legacy = checksums.asset_digest !== undefined
199
- ? normalizeDeclaredDigest(checksums.asset_digest, 'checksums.json.asset_digest')
200
- : null;
201
- if (canonical && legacy && canonical.value !== legacy.value) {
202
- fail(
203
- 'KDNA_RUNTIME_ENTRY_SET_DIGEST_DECLARATION_CONFLICT',
204
- 'checksums.json entry_set_digest conflicts with deprecated asset_digest.',
205
- { entry_set_digest: canonical.value, asset_digest: legacy.value },
206
- );
207
- }
208
- return canonical || legacy;
209
186
  }
210
187
 
211
188
  function comparisonWithDeclarationPriority(observed, declared, external, declarationTarget) {
@@ -232,13 +209,13 @@ function computeDigestEvidenceFromBytes(assetBytes, options = {}) {
232
209
  const checksums = readJsonEntry(asset, 'checksums.json');
233
210
  const assetDigest = computeAssetDigest(bytes);
234
211
  const contentDigest = reader.contentDigestSync(asset);
235
- const entrySetDigest = v1.computeRuntimeEntrySetDigest(
212
+ const entrySetDigest = container.computeRuntimeEntrySetDigest(
236
213
  asset.readEntry('kdna.json'),
237
214
  asset.readEntry('payload.kdnab'),
238
215
  );
239
216
  const externalExpected = options.expectedDigests || {};
240
217
  const expectedAsset = normalizeExpectedDigest(
241
- externalExpected.asset ?? options.expectedAssetDigest,
218
+ externalExpected.asset,
242
219
  'caller',
243
220
  );
244
221
  const externalContent = normalizeExpectedDigest(externalExpected.content, 'caller');
@@ -248,6 +225,7 @@ function computeDigestEvidenceFromBytes(assetBytes, options = {}) {
248
225
 
249
226
  return {
250
227
  profile: DIGEST_PROFILE,
228
+ profile_version: DIGEST_PROFILE_VERSION,
251
229
  asset: {
252
230
  value: assetDigest,
253
231
  basis: BASIS.asset,
@@ -377,63 +355,42 @@ function assertJsonValue(value, code, label) {
377
355
  }
378
356
  }
379
357
 
380
- function assertCapsule1Success(capsule, code = 'KDNA_CAPSULE_2_BUILD_INVALID') {
381
- assertJsonValue(capsule, code, 'Capsule 1');
382
- const validator = schemaValidators().capsule1;
383
- if (!validator(capsule)) {
384
- fail(code, 'Capsule 1 does not satisfy the frozen schema.', schemaErrorDetails(validator));
385
- }
386
- if (capsule.trace.schema_valid !== true) {
387
- fail(code, 'Capsule 1 schema_valid must be true before building Capsule 2.');
388
- }
389
- if (capsule.signature.state !== capsule.trace.signature_state) {
390
- fail(code, 'Capsule 1 signature state conflicts with its trace.');
391
- }
392
- if (capsule.profile !== capsule.trace.profile) {
393
- fail(code, 'Capsule 1 profile conflicts with its trace.');
394
- }
395
- if (!['public', 'licensed', 'remote'].includes(capsule2Access(capsule.access))) {
396
- fail(code, `Capsule 1 access is not a supported Runtime value: ${capsule.access}.`);
397
- }
398
- }
399
-
400
- function assertCapsule2Success(capsule, code = 'KDNA_CAPSULE_ADAPTER_INPUT_INVALID') {
401
- assertJsonValue(capsule, code, 'Capsule 2');
402
- const validator = schemaValidators().capsule2;
358
+ function assertRuntimeCapsuleSuccess(capsule, code = 'KDNA_RUNTIME_CAPSULE_INVALID') {
359
+ assertJsonValue(capsule, code, 'Runtime Capsule');
360
+ const validator = schemaValidators().runtimeCapsule;
403
361
  if (!validator(capsule)) {
404
- fail(code, 'Capsule 2 does not satisfy the successful Runtime schema.', schemaErrorDetails(validator));
362
+ fail(
363
+ code,
364
+ 'Runtime Capsule does not satisfy the current contract.',
365
+ schemaErrorDetails(validator),
366
+ );
405
367
  }
406
368
  assertSuccessfulDigestEvidence(capsule.digests);
407
369
  if (capsule.signature.state !== capsule.trace.signature_state) {
408
- fail(code, 'Capsule 2 signature state conflicts with its trace.');
370
+ fail(code, 'Runtime Capsule signature state conflicts with its trace.');
409
371
  }
410
372
  if (capsule.profile !== capsule.trace.profile) {
411
- fail(code, 'Capsule 2 profile conflicts with its trace.');
412
- }
413
- const capsule1Access = capsule.compatibility?.capsule_1_access;
414
- if (capsule1Access && CAPSULE_1_ACCESS_MAP[capsule1Access] !== capsule.access) {
415
- fail(
416
- code,
417
- 'Capsule 1 compatibility access does not map to Capsule 2 access.',
418
- {
419
- capsule_1_access: capsule1Access,
420
- expected_capsule_2_access: CAPSULE_1_ACCESS_MAP[capsule1Access],
421
- capsule_2_access: capsule.access,
422
- },
423
- );
373
+ fail(code, 'Runtime Capsule profile conflicts with its trace.');
424
374
  }
425
375
  }
426
376
 
427
377
  function assertSuccessfulDigestEvidence(digests) {
428
- if (!digests || digests.profile !== DIGEST_PROFILE) {
429
- fail('KDNA_CAPSULE_2_DIGEST_EVIDENCE_INVALID', 'Capsule 2 digest evidence is missing.');
378
+ if (
379
+ !digests ||
380
+ digests.profile !== DIGEST_PROFILE ||
381
+ digests.profile_version !== DIGEST_PROFILE_VERSION
382
+ ) {
383
+ fail(
384
+ 'KDNA_RUNTIME_CAPSULE_DIGEST_EVIDENCE_INVALID',
385
+ 'Runtime Capsule digest evidence is missing or uses the wrong profile coordinate.',
386
+ );
430
387
  }
431
388
  for (const name of ['asset', 'content', 'runtime_entry_set']) {
432
389
  const item = digests[name];
433
390
  if (!item || !SHA256_RE.test(item.value || '') || item.basis !== BASIS[name]) {
434
391
  fail(
435
- 'KDNA_CAPSULE_2_DIGEST_EVIDENCE_INVALID',
436
- `Capsule 2 ${name} digest evidence is invalid.`,
392
+ 'KDNA_RUNTIME_CAPSULE_DIGEST_EVIDENCE_INVALID',
393
+ `Runtime Capsule ${name} digest evidence is invalid.`,
437
394
  );
438
395
  }
439
396
  const comparisonState = item.comparison && item.comparison.state;
@@ -446,8 +403,8 @@ function assertSuccessfulDigestEvidence(digests) {
446
403
  fail(
447
404
  comparisonState === 'mismatched'
448
405
  ? mismatchCodes[name]
449
- : 'KDNA_CAPSULE_2_DIGEST_EVIDENCE_INVALID',
450
- `Capsule 2 cannot be emitted with ${name} comparison ${comparisonState || 'missing'}.`,
406
+ : 'KDNA_RUNTIME_CAPSULE_DIGEST_EVIDENCE_INVALID',
407
+ `Runtime Capsule cannot be emitted with ${name} comparison ${comparisonState || 'missing'}.`,
451
408
  { digest: name, evidence: item },
452
409
  );
453
410
  }
@@ -458,8 +415,8 @@ function assertSuccessfulDigestEvidence(digests) {
458
415
  !COMPARISON_SOURCES.has(item.comparison.source))
459
416
  ) {
460
417
  fail(
461
- 'KDNA_CAPSULE_2_DIGEST_EVIDENCE_INVALID',
462
- `Capsule 2 ${name} matched comparison is internally inconsistent.`,
418
+ 'KDNA_RUNTIME_CAPSULE_DIGEST_EVIDENCE_INVALID',
419
+ `Runtime Capsule ${name} matched comparison is internally inconsistent.`,
463
420
  );
464
421
  }
465
422
  if (
@@ -469,80 +426,60 @@ function assertSuccessfulDigestEvidence(digests) {
469
426
  item.comparison.source !== null)
470
427
  ) {
471
428
  fail(
472
- 'KDNA_CAPSULE_2_DIGEST_EVIDENCE_INVALID',
473
- `Capsule 2 ${name} not_compared evidence must not claim an expectation.`,
429
+ 'KDNA_RUNTIME_CAPSULE_DIGEST_EVIDENCE_INVALID',
430
+ `Runtime Capsule ${name} not_compared evidence must not claim an expectation.`,
474
431
  );
475
432
  }
476
433
  }
477
434
  }
478
435
 
479
- function capsule2Access(access) {
480
- if (!access || access === 'open') return 'public';
481
- if (access === 'protected') return 'licensed';
482
- if (access === 'runtime') return 'remote';
483
- return access;
436
+ function normalizeRuntimeAccess(access) {
437
+ return access === undefined ? 'public' : access;
484
438
  }
485
439
 
486
- function buildCapsuleV2({ capsule1, manifest, digests, inputKind, loadedAt } = {}) {
487
- if (!capsule1 || capsule1.type !== 'kdna.context.capsule' || capsule1.version !== '1.0') {
488
- fail(
489
- 'KDNA_CAPSULE_2_BUILD_INVALID',
490
- 'Capsule 2 builder requires a frozen Capsule 1 projection.',
491
- );
440
+ function buildRuntimeCapsule({
441
+ projection,
442
+ manifest,
443
+ digests,
444
+ signature,
445
+ inputKind,
446
+ loadedAt,
447
+ schemaValid,
448
+ } = {}) {
449
+ if (!projection || typeof projection !== 'object' || Array.isArray(projection)) {
450
+ fail('KDNA_RUNTIME_CAPSULE_BUILD_INVALID', 'Runtime Capsule builder requires a projection.');
492
451
  }
493
- assertCapsule1Success(capsule1);
494
452
  if (!manifest || typeof manifest !== 'object') {
495
- fail('KDNA_CAPSULE_2_BUILD_INVALID', 'Capsule 2 builder requires the Runtime manifest.');
453
+ fail('KDNA_RUNTIME_CAPSULE_BUILD_INVALID', 'Runtime Capsule builder requires the manifest.');
496
454
  }
497
455
  for (const field of ['asset_id', 'asset_uid', 'version', 'judgment_version']) {
498
456
  if (typeof manifest[field] !== 'string' || manifest[field].length === 0) {
499
- fail('KDNA_CAPSULE_2_BUILD_INVALID', `Runtime manifest ${field} is required.`);
457
+ fail('KDNA_RUNTIME_CAPSULE_BUILD_INVALID', `Runtime manifest ${field} is required.`);
500
458
  }
501
459
  }
502
460
  if (!['packaged_file', 'packaged_bytes'].includes(inputKind)) {
503
- fail('KDNA_CAPSULE_2_BUILD_INVALID', 'Capsule 2 input_kind must identify packaged bytes.');
504
- }
505
- assertSuccessfulDigestEvidence(digests);
506
-
507
- const expectedDomain = manifest.name || manifest.asset_id;
508
- if (capsule1.domain !== expectedDomain) {
509
- fail(
510
- 'KDNA_CAPSULE_2_BUILD_INVALID',
511
- 'Capsule 1 domain does not match the Runtime manifest identity.',
512
- { capsule_1: capsule1.domain, manifest: expectedDomain },
513
- );
514
- }
515
- if (capsule1.judgment_version !== manifest.judgment_version) {
516
461
  fail(
517
- 'KDNA_CAPSULE_2_BUILD_INVALID',
518
- 'Capsule 1 judgment_version does not match the Runtime manifest.',
519
- { capsule_1: capsule1.judgment_version, manifest: manifest.judgment_version },
462
+ 'KDNA_RUNTIME_CAPSULE_BUILD_INVALID',
463
+ 'Runtime Capsule input_kind must identify packaged bytes.',
520
464
  );
521
465
  }
522
- const expectedAccess = manifest.access || 'public';
523
- if (capsule1.access !== expectedAccess) {
466
+ assertSuccessfulDigestEvidence(digests);
467
+ const timestamp = loadedAt || new Date().toISOString();
468
+ if (typeof timestamp !== 'string' || Number.isNaN(Date.parse(timestamp))) {
524
469
  fail(
525
- 'KDNA_CAPSULE_2_BUILD_INVALID',
526
- 'Capsule 1 access does not match the Runtime manifest.',
527
- { capsule_1: capsule1.access, manifest: expectedAccess },
470
+ 'KDNA_RUNTIME_CAPSULE_BUILD_INVALID',
471
+ 'Runtime Capsule loaded_at must be an ISO date-time string.',
528
472
  );
529
473
  }
530
- if (capsule1.asset_digest !== digests.runtime_entry_set.value) {
531
- fail(
532
- 'KDNA_CAPSULE_2_BUILD_INVALID',
533
- 'Capsule 1 asset_digest does not match Runtime entry-set digest E.',
534
- { capsule_1: capsule1.asset_digest, runtime_entry_set: digests.runtime_entry_set.value },
535
- );
536
- }
537
-
538
- const timestamp = loadedAt || capsule1.trace?.loaded_at;
539
- if (typeof timestamp !== 'string' || Number.isNaN(Date.parse(timestamp))) {
540
- fail('KDNA_CAPSULE_2_BUILD_INVALID', 'Capsule 2 loaded_at must be an ISO date-time string.');
474
+ const runtimeAccess = normalizeRuntimeAccess(manifest.access);
475
+ if (!['public', 'licensed', 'remote'].includes(runtimeAccess)) {
476
+ fail('KDNA_RUNTIME_CAPSULE_BUILD_INVALID', `Unsupported Runtime access: ${runtimeAccess}.`);
541
477
  }
478
+ const signatureEvidence = signature || { state: 'absent' };
542
479
 
543
480
  const capsule = {
544
- type: 'kdna.context.capsule',
545
- version: '2.0',
481
+ type: 'kdna.runtime-capsule',
482
+ contract_version: RUNTIME_CAPSULE_CONTRACT_VERSION,
546
483
  asset: {
547
484
  asset_id: manifest.asset_id,
548
485
  asset_uid: manifest.asset_uid,
@@ -550,138 +487,42 @@ function buildCapsuleV2({ capsule1, manifest, digests, inputKind, loadedAt } = {
550
487
  judgment_version: manifest.judgment_version,
551
488
  },
552
489
  digests,
553
- signature: capsule1.signature,
554
- access: capsule2Access(capsule1.access),
555
- risk_level: capsule1.risk_level,
556
- profile: capsule1.profile,
557
- context: capsule1.context,
490
+ signature: signatureEvidence,
491
+ access: runtimeAccess,
492
+ profile: projection.profile,
493
+ context: projection.content || {},
558
494
  trace: {
559
- payload_encoding: capsule1.trace?.payload_encoding || 'cbor',
495
+ payload_encoding: manifest.payload?.encoding || 'cbor',
560
496
  loaded_by: 'kdna-core',
561
497
  loaded_at: timestamp,
562
498
  input_kind: inputKind,
563
499
  runtime_eligible: true,
564
- schema_valid: capsule1.trace?.schema_valid === true,
565
- signature_state: capsule1.trace?.signature_state || capsule1.signature?.state,
566
- profile: capsule1.profile,
500
+ schema_valid: schemaValid === true,
501
+ signature_state: signatureEvidence.state,
502
+ profile: projection.profile,
567
503
  },
568
504
  };
569
-
570
- // Capsule 2 identity is always manifest.asset_id. A distinct legacy name is
571
- // carried only so the one-way v2 -> v1 adapter can reproduce the frozen v1
572
- // `domain` value. It has no routing or identity authority in Capsule 2.
573
- const compatibility = {};
574
- if (capsule1.domain && capsule1.domain !== manifest.asset_id) {
575
- compatibility.capsule_1_domain = capsule1.domain;
576
- }
577
- if (CAPSULE_1_ACCESS_MAP[capsule1.access]) {
578
- compatibility.capsule_1_access = capsule1.access;
579
- }
580
- const capsule1Extensions = {};
581
- for (const key of CAPSULE_1_EXTENSION_KEYS) {
582
- if (Object.prototype.hasOwnProperty.call(capsule1, key)) {
583
- capsule1Extensions[key] = capsule1[key];
584
- }
585
- }
586
- if (Object.keys(capsule1Extensions).length > 0) {
587
- compatibility.capsule_1_extensions = capsule1Extensions;
588
- }
589
- if (Object.keys(compatibility).length > 0) {
590
- capsule.compatibility = compatibility;
591
- }
592
-
593
- assertCapsule2Success(capsule, 'KDNA_CAPSULE_2_BUILD_INVALID');
505
+ assertRuntimeCapsuleSuccess(capsule, 'KDNA_RUNTIME_CAPSULE_BUILD_INVALID');
594
506
  return capsule;
595
507
  }
596
508
 
597
- function adaptCapsuleV2ToV1(capsule) {
598
- if (!capsule || capsule.type !== 'kdna.context.capsule' || capsule.version !== '2.0') {
599
- fail('KDNA_CAPSULE_ADAPTER_INPUT_INVALID', 'The adapter requires a Runtime Capsule 2 value.');
600
- }
601
- assertCapsule2Success(capsule);
602
- const domain = capsule.compatibility?.capsule_1_domain || capsule.asset?.asset_id;
603
- if (typeof domain !== 'string' || domain.length === 0) {
604
- fail('KDNA_CAPSULE_ADAPTER_INPUT_INVALID', 'Capsule 2 has no Capsule 1 domain mapping.');
605
- }
606
- const capsule1 = {
607
- type: 'kdna.context.capsule',
608
- version: '1.0',
609
- domain,
610
- judgment_version: capsule.asset.judgment_version,
611
- asset_digest: capsule.digests.runtime_entry_set.value,
612
- signature: capsule.signature,
613
- access: capsule.compatibility?.capsule_1_access || capsule.access,
614
- risk_level: capsule.risk_level,
615
- profile: capsule.profile,
616
- context: capsule.context,
617
- trace: {
618
- payload_encoding: capsule.trace.payload_encoding,
619
- loaded_by: capsule.trace.loaded_by,
620
- loaded_at: capsule.trace.loaded_at,
621
- schema_valid: capsule.trace.schema_valid,
622
- signature_state: capsule.trace.signature_state,
623
- profile: capsule.trace.profile,
624
- },
625
- };
626
- const capsule1Extensions = capsule.compatibility?.capsule_1_extensions;
627
- if (capsule1Extensions) {
628
- for (const key of CAPSULE_1_EXTENSION_KEYS) {
629
- if (Object.prototype.hasOwnProperty.call(capsule1Extensions, key)) {
630
- capsule1[key] = capsule1Extensions[key];
631
- }
632
- }
633
- }
634
- assertCapsule1Success(capsule1, 'KDNA_CAPSULE_ADAPTER_OUTPUT_INVALID');
635
- return capsule1;
636
- }
637
-
638
- function loadCapsuleV2(input, options = {}) {
639
- const { bytes, inputKind } = snapshotPackagedBytes(input);
640
- let validation;
641
- try {
642
- validation = v1.validate(bytes);
643
- } catch (error) {
644
- fail('KDNA_CAPSULE_2_ASSET_INVALID', error.message);
645
- }
646
- if (validation.overall_valid !== true) {
647
- fail(
648
- 'KDNA_CAPSULE_2_ASSET_INVALID',
649
- `Cannot emit Capsule 2 from an invalid asset: ${validation.problems.join('; ')}`,
650
- validation,
651
- );
652
- }
653
-
654
- const digests = computeDigestEvidenceFromBytes(bytes, {
655
- expectedDigests: options.expectedDigests,
656
- expectedAssetDigest: options.expectedAssetDigest,
657
- });
658
- assertSuccessfulDigestEvidence(digests);
659
- const reader = createKdnaAssetReader();
660
- const asset = reader.openSync(bytes);
661
- const manifest = reader.readManifestSync(asset);
662
- const capsule1 = v1.loadAuthorized(bytes, {
663
- ...options,
664
- as: 'json',
665
- });
666
- return buildCapsuleV2({
667
- capsule1,
668
- manifest,
669
- digests,
670
- inputKind,
671
- loadedAt: options.loadedAt,
672
- });
509
+ function loadRuntimeCapsule(input, options = {}) {
510
+ snapshotPackagedBytes(input);
511
+ return container.loadAuthorized(input, { ...options, as: 'json' });
673
512
  }
674
513
 
675
514
  module.exports = {
676
515
  BASIS,
677
516
  CAPSULE_DIGEST_PROFILE,
517
+ CANONICALIZATION_PROFILE_VERSION,
678
518
  DIGEST_PROFILE,
679
- adaptCapsuleV2ToV1,
680
- buildCapsuleV2,
519
+ DIGEST_PROFILE_VERSION,
520
+ RUNTIME_CAPSULE_CONTRACT_VERSION,
521
+ buildRuntimeCapsule,
681
522
  canonicalizeJcs,
682
523
  computeAssetDigest,
683
524
  computeCapsuleDeliveryDigest,
684
525
  computeDigestEvidence,
685
- computeRuntimeEntrySetDigest: v1.computeRuntimeEntrySetDigest,
686
- loadCapsuleV2,
526
+ computeRuntimeEntrySetDigest: container.computeRuntimeEntrySetDigest,
527
+ loadRuntimeCapsule,
687
528
  };