@aikdna/kdna-core 0.4.0 → 0.6.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.
@@ -0,0 +1,106 @@
1
+ const crypto = require('crypto');
2
+
3
+ const LICENSED_ENTRY_PROFILE = 'kdna-licensed-entry-v1';
4
+ const KDF = 'scrypt-sha256';
5
+ const ALG = 'AES-256-GCM';
6
+
7
+ function toBuffer(value, label) {
8
+ if (Buffer.isBuffer(value)) return value;
9
+ if (value instanceof Uint8Array) return Buffer.from(value);
10
+ if (typeof value === 'string') return Buffer.from(value, 'utf8');
11
+ throw new Error(`${label} must be a string, Buffer, or Uint8Array`);
12
+ }
13
+
14
+ function decodeBase64(value, label) {
15
+ if (typeof value !== 'string' || !value) throw new Error(`${label} must be base64`);
16
+ return Buffer.from(value, 'base64');
17
+ }
18
+
19
+ function normalizeEnvelope(value) {
20
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
21
+ return JSON.parse(Buffer.from(value).toString('utf8'));
22
+ }
23
+ if (typeof value === 'string') return JSON.parse(value);
24
+ if (value && typeof value === 'object') return value;
25
+ throw new Error('encrypted entry envelope must be JSON');
26
+ }
27
+
28
+ function deriveLicensedEntryKey(options = {}) {
29
+ const { licenseKey, machineFingerprint, salt, keyLength = 32 } = options;
30
+ if (!licenseKey) throw new Error('licenseKey is required');
31
+ if (!machineFingerprint) throw new Error('machineFingerprint is required');
32
+ const saltBuffer = Buffer.isBuffer(salt) || salt instanceof Uint8Array
33
+ ? Buffer.from(salt)
34
+ : decodeBase64(salt, 'salt');
35
+ const secret = `${licenseKey}|${machineFingerprint}`;
36
+ return crypto.scryptSync(secret, saltBuffer, keyLength);
37
+ }
38
+
39
+ function encryptedEntryAad(entryName, manifest = {}) {
40
+ return Buffer.from(
41
+ [
42
+ LICENSED_ENTRY_PROFILE,
43
+ manifest.name || manifest.asset_id || '',
44
+ manifest.version || '',
45
+ entryName,
46
+ ].join('\n'),
47
+ 'utf8',
48
+ );
49
+ }
50
+
51
+ function encryptLicensedEntry(plaintext, options = {}) {
52
+ const { entryName, manifest = {}, licenseKey, machineFingerprint } = options;
53
+ if (!entryName) throw new Error('entryName is required');
54
+ const salt = crypto.randomBytes(16);
55
+ const iv = crypto.randomBytes(12);
56
+ const key = deriveLicensedEntryKey({ licenseKey, machineFingerprint, salt });
57
+ const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
58
+ cipher.setAAD(encryptedEntryAad(entryName, manifest));
59
+ const ciphertext = Buffer.concat([cipher.update(toBuffer(plaintext, 'plaintext')), cipher.final()]);
60
+ return {
61
+ profile: LICENSED_ENTRY_PROFILE,
62
+ alg: ALG,
63
+ kdf: KDF,
64
+ salt: salt.toString('base64'),
65
+ iv: iv.toString('base64'),
66
+ tag: cipher.getAuthTag().toString('base64'),
67
+ ciphertext: ciphertext.toString('base64'),
68
+ };
69
+ }
70
+
71
+ function decryptLicensedEntry(envelopeValue, options = {}) {
72
+ const { entryName, manifest = {}, licenseKey, machineFingerprint } = options;
73
+ if (!entryName) throw new Error('entryName is required');
74
+ const envelope = normalizeEnvelope(envelopeValue);
75
+ if (envelope.profile !== LICENSED_ENTRY_PROFILE) {
76
+ throw new Error(`unsupported encrypted entry profile: ${envelope.profile || 'unknown'}`);
77
+ }
78
+ if (envelope.alg !== ALG) throw new Error(`unsupported encrypted entry alg: ${envelope.alg}`);
79
+ if (envelope.kdf !== KDF) throw new Error(`unsupported encrypted entry kdf: ${envelope.kdf}`);
80
+ const key = deriveLicensedEntryKey({
81
+ licenseKey,
82
+ machineFingerprint,
83
+ salt: envelope.salt,
84
+ });
85
+ const decipher = crypto.createDecipheriv('aes-256-gcm', key, decodeBase64(envelope.iv, 'iv'));
86
+ decipher.setAAD(encryptedEntryAad(entryName, manifest));
87
+ decipher.setAuthTag(decodeBase64(envelope.tag, 'tag'));
88
+ return Buffer.concat([
89
+ decipher.update(decodeBase64(envelope.ciphertext, 'ciphertext')),
90
+ decipher.final(),
91
+ ]);
92
+ }
93
+
94
+ function createLicensedDecryptEntry(options = {}) {
95
+ const { licenseKey, machineFingerprint } = options;
96
+ return ({ entryName, ciphertext, manifest }) =>
97
+ decryptLicensedEntry(ciphertext, { entryName, manifest, licenseKey, machineFingerprint });
98
+ }
99
+
100
+ module.exports = {
101
+ LICENSED_ENTRY_PROFILE,
102
+ deriveLicensedEntryKey,
103
+ encryptLicensedEntry,
104
+ decryptLicensedEntry,
105
+ createLicensedDecryptEntry,
106
+ };
package/src/index.js CHANGED
@@ -6,11 +6,17 @@ const lint = require('./lint-pure');
6
6
  const validate = require('./validate-pure');
7
7
  const render = require('./render');
8
8
  const compose = require('./compose');
9
+ const assetReader = require('./asset-reader');
10
+ const cryptoProfile = require('./crypto-profile');
11
+ const publicApi = require('./public-api');
9
12
 
10
13
  module.exports = {
14
+ ...publicApi,
11
15
  ...loader,
12
16
  ...lint,
13
17
  ...validate,
14
18
  ...render,
15
19
  ...compose,
20
+ ...assetReader,
21
+ ...cryptoProfile,
16
22
  };
package/src/index.mjs CHANGED
@@ -17,3 +17,14 @@ export { validateDomainSchema, validateCrossFile } from './validate-pure.js';
17
17
  export { renderPreviewHTML, escHtml, renderCard } from './render.js';
18
18
 
19
19
  export { composeContext, composeContextWithAttribution, classifySignals, classifySignalsAcrossDomains, composeChecks, loadAndCompose, loadCluster, detectDomainConflicts, generateClusterTrace } from './compose.js';
20
+
21
+ import assetReader from './asset-reader.js';
22
+ import cryptoProfile from './crypto-profile.js';
23
+
24
+ export const STANDARD_ENTRIES = assetReader.STANDARD_ENTRIES;
25
+ export const createKdnaAssetReader = assetReader.createKdnaAssetReader;
26
+ export const LICENSED_ENTRY_PROFILE = cryptoProfile.LICENSED_ENTRY_PROFILE;
27
+ export const deriveLicensedEntryKey = cryptoProfile.deriveLicensedEntryKey;
28
+ export const encryptLicensedEntry = cryptoProfile.encryptLicensedEntry;
29
+ export const decryptLicensedEntry = cryptoProfile.decryptLicensedEntry;
30
+ export const createLicensedDecryptEntry = cryptoProfile.createLicensedDecryptEntry;
package/src/lint-pure.js CHANGED
@@ -261,11 +261,20 @@ const VALID_BADGE = new Set(['untested', 'tested', 'validated', 'expert_reviewed
261
261
  const VALID_ACCESS = new Set(['open', 'licensed', 'runtime']);
262
262
  const VALID_RISK = new Set(['R0', 'R1', 'R2', 'R3']);
263
263
  const VALID_I18N = new Set(['L0', 'L1', 'L2', 'L3']);
264
+ const VALID_PRIVACY = new Set(['public', 'private', 'sensitive', 'regulated']);
265
+ const VALID_ASSET_TYPE = new Set([
266
+ 'domain_judgment',
267
+ 'personal_judgment',
268
+ 'organization_standard',
269
+ 'team_policy',
270
+ 'creator_style',
271
+ 'risk_guard',
272
+ ]);
264
273
 
265
274
  const MANIFEST_REQUIRED = [
266
- 'kdna_spec', 'name', 'version', 'judgment_version',
267
- 'description', 'author', 'license', 'status',
268
- 'quality_badge', 'access', 'language',
275
+ 'format', 'format_version', 'spec_version', 'name', 'version',
276
+ 'judgment_version', 'description', 'author', 'license', 'status',
277
+ 'quality_badge', 'access', 'languages', 'default_language',
269
278
  ];
270
279
 
271
280
  /**
@@ -283,13 +292,15 @@ function validateManifest(manifest) {
283
292
  return { errors, warnings };
284
293
  }
285
294
 
286
- // 1. Check spec_version is NOT in domain manifest (use kdna_spec only)
287
- if ('spec_version' in manifest) {
295
+ // 1. Check disallowed pre-v1.0 manifest aliases
296
+ if ('kdna_spec' in manifest) {
288
297
  errors.push(
289
- 'kdna.json: spec_version is deprecated in domain manifests. Use kdna_spec. ' +
290
- '(spec_version is reserved for .kdna container manifests only.)',
298
+ 'kdna.json: kdna_spec is not allowed. Use spec_version.',
291
299
  );
292
300
  }
301
+ if ('language' in manifest) {
302
+ errors.push('kdna.json: language is not allowed. Use default_language and languages.');
303
+ }
293
304
 
294
305
  // 2. Check required fields
295
306
  for (const field of MANIFEST_REQUIRED) {
@@ -304,6 +315,14 @@ function validateManifest(manifest) {
304
315
  }
305
316
 
306
317
  // 4. Validate enum fields
318
+ if (manifest.format && manifest.format !== 'kdna') {
319
+ errors.push(`kdna.json.format: invalid value "${manifest.format}". Expected "kdna".`);
320
+ }
321
+ if (manifest.format_version && manifest.format_version !== '1.0') {
322
+ errors.push(
323
+ `kdna.json.format_version: invalid value "${manifest.format_version}". Expected "1.0".`,
324
+ );
325
+ }
307
326
  if (manifest.status && !VALID_STATUS.has(manifest.status)) {
308
327
  errors.push(
309
328
  `kdna.json.status: invalid value "${manifest.status}". ` +
@@ -334,6 +353,18 @@ function validateManifest(manifest) {
334
353
  `Valid: ${[...VALID_I18N].join(', ')}`,
335
354
  );
336
355
  }
356
+ if (manifest.privacy_level && !VALID_PRIVACY.has(manifest.privacy_level)) {
357
+ warnings.push(
358
+ `kdna.json.privacy_level: non-standard value "${manifest.privacy_level}". ` +
359
+ `Valid: ${[...VALID_PRIVACY].join(', ')}`,
360
+ );
361
+ }
362
+ if (manifest.asset_type && !VALID_ASSET_TYPE.has(manifest.asset_type)) {
363
+ warnings.push(
364
+ `kdna.json.asset_type: non-standard value "${manifest.asset_type}". ` +
365
+ `Valid: ${[...VALID_ASSET_TYPE].join(', ')}`,
366
+ );
367
+ }
337
368
 
338
369
  // 5. Deprecated status must have replaced_by
339
370
  if (manifest.status === 'deprecated' && !manifest.replaced_by) {
@@ -362,10 +393,10 @@ function validateManifest(manifest) {
362
393
  errors.push('kdna.json.license: missing "type"');
363
394
  }
364
395
 
365
- // 9. Validate kdna_spec value
366
- if (manifest.kdna_spec && manifest.kdna_spec !== '1.0-rc') {
396
+ // 9. Validate spec_version value
397
+ if (manifest.spec_version && manifest.spec_version !== '1.0-rc') {
367
398
  warnings.push(
368
- `kdna.json.kdna_spec: non-standard value "${manifest.kdna_spec}". Expected "1.0-rc".`,
399
+ `kdna.json.spec_version: non-standard value "${manifest.spec_version}". Expected "1.0-rc".`,
369
400
  );
370
401
  }
371
402
 
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Stable public API for third-party KDNA integrations.
3
+ *
4
+ * Lower-level modules remain exported for advanced runtimes, but external
5
+ * adapters should prefer these names. They encode the asset-first contract:
6
+ * callers pass .kdna files or bytes, and no persistent directory extraction is
7
+ * required.
8
+ */
9
+
10
+ const { createKdnaAssetReader } = require('./asset-reader');
11
+ const { lintDomain } = require('./lint-pure');
12
+ const { validateCrossFile, validateDomainSchema } = require('./validate-pure');
13
+ const { formatContext } = require('./loader');
14
+ const {
15
+ composeContextWithAttribution,
16
+ detectDomainConflicts,
17
+ classifySignalsAcrossDomains,
18
+ generateClusterTrace,
19
+ } = require('./compose');
20
+
21
+ function readerFrom(options = {}) {
22
+ return options.reader || createKdnaAssetReader();
23
+ }
24
+
25
+ function isAsset(value) {
26
+ return value && typeof value === 'object' && value.entries instanceof Map && typeof value.readEntry === 'function';
27
+ }
28
+
29
+ async function asAsset(input, options = {}) {
30
+ if (isAsset(input)) return input;
31
+ return readerFrom(options).open(input);
32
+ }
33
+
34
+ function asAssetSync(input, options = {}) {
35
+ if (isAsset(input)) return input;
36
+ return readerFrom(options).openSync(input);
37
+ }
38
+
39
+ async function openKDNA(input, options = {}) {
40
+ return asAsset(input, options);
41
+ }
42
+
43
+ function openKDNASync(input, options = {}) {
44
+ return asAssetSync(input, options);
45
+ }
46
+
47
+ async function inspectKDNA(input, options = {}) {
48
+ const reader = readerFrom(options);
49
+ const asset = await asAsset(input, { ...options, reader });
50
+ const profile = await reader.loadProfile(asset, 'index', options);
51
+ const verification = options.verify === false ? null : await reader.verify(asset, options);
52
+ return {
53
+ name: profile.name,
54
+ version: profile.version,
55
+ judgment_version: profile.judgment_version,
56
+ access: profile.manifest.access || 'open',
57
+ status: profile.manifest.status || null,
58
+ quality_badge: profile.manifest.quality_badge || null,
59
+ risk_level: profile.manifest.risk_level || null,
60
+ keywords: profile.keywords,
61
+ entries: profile.entries,
62
+ asset_digest: profile.asset_digest,
63
+ content_digest: profile.content_digest,
64
+ signature_valid: verification ? verification.signature_valid : null,
65
+ ok: verification ? verification.ok : null,
66
+ errors: verification ? verification.errors : [],
67
+ warnings: verification ? verification.warnings : [],
68
+ manifest: profile.manifest,
69
+ };
70
+ }
71
+
72
+ function inspectKDNASync(input, options = {}) {
73
+ const reader = readerFrom(options);
74
+ const asset = asAssetSync(input, { ...options, reader });
75
+ const profile = reader.loadProfileSync(asset, 'index', options);
76
+ const verification = options.verify === false ? null : reader.verifySync(asset, options);
77
+ return {
78
+ name: profile.name,
79
+ version: profile.version,
80
+ judgment_version: profile.judgment_version,
81
+ access: profile.manifest.access || 'open',
82
+ status: profile.manifest.status || null,
83
+ quality_badge: profile.manifest.quality_badge || null,
84
+ risk_level: profile.manifest.risk_level || null,
85
+ keywords: profile.keywords,
86
+ entries: profile.entries,
87
+ asset_digest: profile.asset_digest,
88
+ content_digest: profile.content_digest,
89
+ signature_valid: verification ? verification.signature_valid : null,
90
+ ok: verification ? verification.ok : null,
91
+ errors: verification ? verification.errors : [],
92
+ warnings: verification ? verification.warnings : [],
93
+ manifest: profile.manifest,
94
+ };
95
+ }
96
+
97
+ async function loadKDNA(input, options = {}) {
98
+ const reader = readerFrom(options);
99
+ const asset = await asAsset(input, { ...options, reader });
100
+ return reader.loadProfile(asset, options.profile || 'compact', options);
101
+ }
102
+
103
+ function loadKDNASync(input, options = {}) {
104
+ const reader = readerFrom(options);
105
+ const asset = asAssetSync(input, { ...options, reader });
106
+ return reader.loadProfileSync(asset, options.profile || 'compact', options);
107
+ }
108
+
109
+ async function validateKDNA(input, options = {}) {
110
+ const reader = readerFrom(options);
111
+ const asset = await asAsset(input, { ...options, reader });
112
+ const assetResult = await reader.verify(asset, options);
113
+ let dataMap = null;
114
+ let lintResult = { errors: [], warnings: [] };
115
+ let schemaResult = { errors: [], warnings: [] };
116
+ let crossFileResult = { errors: [], warnings: [] };
117
+
118
+ try {
119
+ dataMap = await reader.readDataMap(asset, undefined, options);
120
+ lintResult = lintDomain(dataMap);
121
+ schemaResult = validateDomainSchema(dataMap, options.schemas || {});
122
+ crossFileResult = validateCrossFile(dataMap);
123
+ } catch (e) {
124
+ lintResult.errors.push(e.message);
125
+ }
126
+
127
+ const errors = [
128
+ ...assetResult.errors,
129
+ ...lintResult.errors,
130
+ ...schemaResult.errors,
131
+ ...crossFileResult.errors,
132
+ ];
133
+ const warnings = [
134
+ ...assetResult.warnings,
135
+ ...lintResult.warnings,
136
+ ...schemaResult.warnings,
137
+ ...crossFileResult.warnings,
138
+ ];
139
+ return {
140
+ ok: errors.length === 0,
141
+ errors,
142
+ warnings,
143
+ asset: assetResult,
144
+ lint: lintResult,
145
+ schema: schemaResult,
146
+ cross_file: crossFileResult,
147
+ };
148
+ }
149
+
150
+ function validateKDNASync(input, options = {}) {
151
+ const reader = readerFrom(options);
152
+ const asset = asAssetSync(input, { ...options, reader });
153
+ const assetResult = reader.verifySync(asset, options);
154
+ let lintResult = { errors: [], warnings: [] };
155
+ let schemaResult = { errors: [], warnings: [] };
156
+ let crossFileResult = { errors: [], warnings: [] };
157
+
158
+ try {
159
+ const dataMap = reader.readDataMapSync(asset, undefined, options);
160
+ lintResult = lintDomain(dataMap);
161
+ schemaResult = validateDomainSchema(dataMap, options.schemas || {});
162
+ crossFileResult = validateCrossFile(dataMap);
163
+ } catch (e) {
164
+ lintResult.errors.push(e.message);
165
+ }
166
+
167
+ const errors = [
168
+ ...assetResult.errors,
169
+ ...lintResult.errors,
170
+ ...schemaResult.errors,
171
+ ...crossFileResult.errors,
172
+ ];
173
+ const warnings = [
174
+ ...assetResult.warnings,
175
+ ...lintResult.warnings,
176
+ ...schemaResult.warnings,
177
+ ...crossFileResult.warnings,
178
+ ];
179
+ return {
180
+ ok: errors.length === 0,
181
+ errors,
182
+ warnings,
183
+ asset: assetResult,
184
+ lint: lintResult,
185
+ schema: schemaResult,
186
+ cross_file: crossFileResult,
187
+ };
188
+ }
189
+
190
+ async function renderForAgent(input, options = {}) {
191
+ const loaded = await loadKDNA(input, options);
192
+ if (loaded.context != null) return loaded.context;
193
+ return loaded.domain ? formatContext(loaded.domain) : '';
194
+ }
195
+
196
+ function renderForAgentSync(input, options = {}) {
197
+ const loaded = loadKDNASync(input, options);
198
+ if (loaded.context != null) return loaded.context;
199
+ return loaded.domain ? formatContext(loaded.domain) : '';
200
+ }
201
+
202
+ async function verifyAsset(input, options = {}) {
203
+ const reader = readerFrom(options);
204
+ const asset = await asAsset(input, { ...options, reader });
205
+ return reader.verify(asset, options);
206
+ }
207
+
208
+ function verifyAssetSync(input, options = {}) {
209
+ const reader = readerFrom(options);
210
+ const asset = asAssetSync(input, { ...options, reader });
211
+ return reader.verifySync(asset, options);
212
+ }
213
+
214
+ async function verifyDigest(input, expectedDigest, options = {}) {
215
+ return verifyAsset(input, { ...options, asset_digest: expectedDigest });
216
+ }
217
+
218
+ function verifyDigestSync(input, expectedDigest, options = {}) {
219
+ return verifyAssetSync(input, { ...options, asset_digest: expectedDigest });
220
+ }
221
+
222
+ async function verifySignature(input, options = {}) {
223
+ return verifyAsset(input, { ...options, requireSignature: true });
224
+ }
225
+
226
+ function verifySignatureSync(input, options = {}) {
227
+ return verifyAssetSync(input, { ...options, requireSignature: true });
228
+ }
229
+
230
+ function scoreMatch(input, info) {
231
+ const haystack = String(input || '').toLowerCase();
232
+ const terms = [
233
+ info.name,
234
+ ...(info.keywords || []),
235
+ info.manifest?.description,
236
+ info.manifest?.core_insight,
237
+ ]
238
+ .filter(Boolean)
239
+ .map((v) => String(v).toLowerCase());
240
+ const matched = terms.filter((term) => term && haystack.includes(term.replace(/^@[^/]+\//, '')));
241
+ return { score: matched.length, matched };
242
+ }
243
+
244
+ async function matchDomain(input, candidates, options = {}) {
245
+ const results = [];
246
+ for (const candidate of candidates || []) {
247
+ const info = typeof candidate === 'string' || candidate instanceof Uint8Array || isAsset(candidate)
248
+ ? await inspectKDNA(candidate, { ...options, verify: false })
249
+ : candidate;
250
+ const match = scoreMatch(input, info);
251
+ if (match.score > 0) results.push({ ...info, score: match.score, matched: match.matched });
252
+ }
253
+ return results.sort((a, b) => b.score - a.score || String(a.name).localeCompare(String(b.name)));
254
+ }
255
+
256
+ function matchDomainSync(input, candidates, options = {}) {
257
+ const results = [];
258
+ for (const candidate of candidates || []) {
259
+ const info = typeof candidate === 'string' || candidate instanceof Uint8Array || isAsset(candidate)
260
+ ? inspectKDNASync(candidate, { ...options, verify: false })
261
+ : candidate;
262
+ const match = scoreMatch(input, info);
263
+ if (match.score > 0) results.push({ ...info, score: match.score, matched: match.matched });
264
+ }
265
+ return results.sort((a, b) => b.score - a.score || String(a.name).localeCompare(String(b.name)));
266
+ }
267
+
268
+ async function composeKDNA(inputs, options = {}) {
269
+ const loaded = [];
270
+ for (const input of inputs || []) {
271
+ const profile = await loadKDNA(input, { ...options, profile: options.profile || 'compact', context: false });
272
+ if (profile.domain) {
273
+ loaded.push({
274
+ id: profile.manifest.name || profile.domain.core?.meta?.domain,
275
+ name: profile.manifest.name || profile.domain.core?.meta?.domain,
276
+ manifest: profile.manifest,
277
+ ...profile.domain,
278
+ });
279
+ }
280
+ }
281
+ const { selected, excluded } = classifySignalsAcrossDomains(options.input || '', loaded);
282
+ const selectedIds = new Set(selected.map((d) => d.id));
283
+ const activeDomains = options.input ? loaded.filter((d) => selectedIds.has(d.id)) : loaded;
284
+ const conflicts = detectDomainConflicts(activeDomains);
285
+ const { context, attributionMap } = composeContextWithAttribution(activeDomains, options);
286
+ return {
287
+ domains: loaded,
288
+ activeDomains,
289
+ selected,
290
+ excluded,
291
+ conflicts,
292
+ context,
293
+ attributionMap,
294
+ trace: generateClusterTrace({
295
+ input: options.input || '',
296
+ loadedDomains: loaded,
297
+ activeDomains,
298
+ conflicts,
299
+ }),
300
+ };
301
+ }
302
+
303
+ module.exports = {
304
+ openKDNA,
305
+ openKDNASync,
306
+ inspectKDNA,
307
+ inspectKDNASync,
308
+ loadKDNA,
309
+ loadKDNASync,
310
+ validateKDNA,
311
+ validateKDNASync,
312
+ renderForAgent,
313
+ renderForAgentSync,
314
+ verifyAsset,
315
+ verifyAssetSync,
316
+ verifyDigest,
317
+ verifyDigestSync,
318
+ verifySignature,
319
+ verifySignatureSync,
320
+ matchDomain,
321
+ matchDomainSync,
322
+ composeKDNA,
323
+ };