@aifabrix/builder 2.44.2 → 2.44.3

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/.npmrc.token CHANGED
@@ -1 +1 @@
1
- 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
1
+ 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
@@ -22,7 +22,7 @@
22
22
  * @property {string} [issuedBy]
23
23
  * @property {string} [licenseLevelIssuer]
24
24
  * @property {string} [dataplaneVersion]
25
- * @property {('RS256'|'HS256')} [algorithm] RS256 production; HS256 local dev HMAC signer only
25
+ * @property {'RS256'} [algorithm] Integration certificate signing algorithm (RS256 only)
26
26
  * @property {string|null} [publicKey]
27
27
  * @property {string|null} [publicKeyFingerprint]
28
28
  * @property {Object} [metadata]
@@ -11,7 +11,6 @@
11
11
 
12
12
  const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
- const { truncatePublicKeyPreview } = require('./certification-show-enrich');
15
14
 
16
15
  function logSourceAndHeader(summary) {
17
16
  const isOffline = summary.source === 'offline';
@@ -263,23 +262,51 @@ function displayExternalAppBlock(summary) {
263
262
  }
264
263
 
265
264
  /**
266
- * Local certification + optional verify rows (external integrations).
267
- * @param {Object} summary
265
+ * Colored certification outcome from `status` string.
266
+ * @param {string} raw - lowercased status
267
+ * @returns {string}
268
268
  */
269
- function logCertificationSection(summary) {
270
- if (!summary.isExternal) return;
271
- logger.log('');
272
- logger.log('🪪 Certification (local system file)');
273
- const c = summary.localCertification;
274
- if (!c || typeof c !== 'object') {
275
- logger.log(' (none or unreadable)');
276
- } else {
277
- logger.log(` enabled: ${c.enabled}`);
278
- logger.log(` algorithm: ${c.algorithm ?? '—'}`);
279
- logger.log(` issuer: ${c.issuer ?? '—'}`);
280
- logger.log(` version: ${c.version ?? '—'}`);
281
- logger.log(` publicKey: ${truncatePublicKeyPreview(c.publicKey, 64)}`);
269
+ function formatCertificationOutcome(raw) {
270
+ if (raw === 'passed') return chalk.green('passed');
271
+ if (raw === 'not_passed') return chalk.red('not_passed');
272
+ if (raw === 'pending') return chalk.yellow('pending');
273
+ return raw || '—';
274
+ }
275
+
276
+ /**
277
+ * Certification status line: enabled ✔/✖ plus outcome (passed / not_passed / pending).
278
+ * @param {Object} c - certification block from system file
279
+ * @returns {string}
280
+ */
281
+ function formatCertificationStatusLine(c) {
282
+ const enabledTrue = c.enabled === true;
283
+ const enabledSym = enabledTrue ? chalk.green('✔') : chalk.red('✖');
284
+ const raw = (c.status && String(c.status).toLowerCase()) || '';
285
+ const outcome = formatCertificationOutcome(raw);
286
+ return ` Status: ${enabledSym} ${outcome}`;
287
+ }
288
+
289
+ /**
290
+ * Local certification fields from system file (TTY block).
291
+ * @param {Object} c
292
+ */
293
+ function logCertificationLocalFileDetails(c) {
294
+ logger.log(formatCertificationStatusLine(c));
295
+ const levelStr =
296
+ c.level === undefined || c.level === null ? '' : String(c.level).trim();
297
+ if (levelStr) {
298
+ logger.log(` Level: ${levelStr}`);
282
299
  }
300
+ logger.log(` algorithm: ${c.algorithm ?? '—'}`);
301
+ logger.log(` issuer: ${c.issuer ?? '—'}`);
302
+ logger.log(` version: ${c.version ?? '—'}`);
303
+ }
304
+
305
+ /**
306
+ * Dataplane certification verify rows (when `--verify-cert` populated summary).
307
+ * @param {Object} summary
308
+ */
309
+ function logCertificationVerifySection(summary) {
283
310
  if (summary.certificationVerifyRows && summary.certificationVerifyRows.length > 0) {
284
311
  logger.log('');
285
312
  logger.log('🪪 Certification verify (dataplane)');
@@ -303,6 +330,23 @@ function logCertificationSection(summary) {
303
330
  }
304
331
  }
305
332
 
333
+ /**
334
+ * Local certification + optional verify rows (external integrations).
335
+ * @param {Object} summary
336
+ */
337
+ function logCertificationSection(summary) {
338
+ if (!summary.isExternal) return;
339
+ logger.log('');
340
+ logger.log('🪪 Certification (local system file)');
341
+ const c = summary.localCertification;
342
+ if (!c || typeof c !== 'object') {
343
+ logger.log(' (none or unreadable)');
344
+ } else {
345
+ logCertificationLocalFileDetails(c);
346
+ }
347
+ logCertificationVerifySection(summary);
348
+ }
349
+
306
350
  /**
307
351
  * Format and print human-readable show output (offline or online).
308
352
  * @param {Object} summary - Unified summary (buildOfflineSummaryFromDeployJson or buildOnlineSummary)
@@ -38,14 +38,14 @@ function pickArtifactForCertificationMerge(artifacts) {
38
38
  }
39
39
 
40
40
  /**
41
- * @param {string} algorithmUpper
42
41
  * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
42
+ * @param {Object} ex
43
43
  * @returns {string}
44
44
  */
45
- function hs256DevPublicKeyPlaceholder(algorithmUpper, art) {
46
- if (algorithmUpper !== 'HS256') return '';
47
- const cid = certificateIdToString(art.certificateId);
48
- return `HS256-DEV-NO-PEM:${cid || 'integration-certificate'}`;
45
+ function resolvePublicKey(art, ex) {
46
+ const fromArt = trimOrEmpty(art.publicKey);
47
+ if (fromArt) return fromArt;
48
+ return trimOrEmpty(ex.publicKey);
49
49
  }
50
50
 
51
51
  /**
@@ -53,13 +53,34 @@ function hs256DevPublicKeyPlaceholder(algorithmUpper, art) {
53
53
  * @param {Object} ex
54
54
  * @returns {string}
55
55
  */
56
- function resolvePublicKey(art, ex) {
57
- const fromArt = trimOrEmpty(art.publicKey);
56
+ function resolvePublicKeyFingerprint(art, ex) {
57
+ const fromArt = trimOrEmpty(art.publicKeyFingerprint);
58
58
  if (fromArt) return fromArt;
59
- const fromExisting = trimOrEmpty(ex.publicKey);
60
- if (fromExisting) return fromExisting;
61
- const algorithmUpper = trimOrEmpty(art.algorithm).toUpperCase();
62
- return hs256DevPublicKeyPlaceholder(algorithmUpper, art);
59
+ return trimOrEmpty(ex.publicKeyFingerprint);
60
+ }
61
+
62
+ /**
63
+ * @param {string} raw
64
+ * @returns {string} Normalized digest or empty when invalid
65
+ */
66
+ function normalizeContractHash(raw) {
67
+ const s = trimOrEmpty(raw);
68
+ return CONTRACT_HASH_PATTERN.test(s) ? s : '';
69
+ }
70
+
71
+ /**
72
+ * @param {import('../api/types/certificates.types').CertificateArtifactResponse} art
73
+ * @param {Object} ex
74
+ * @returns {string}
75
+ */
76
+ function resolveContractHash(art, ex) {
77
+ return (
78
+ normalizeContractHash(art.contractHash) ||
79
+ normalizeContractHash(art.integrationHash) ||
80
+ normalizeContractHash(ex.contractHash) ||
81
+ normalizeContractHash(ex.integrationHash) ||
82
+ ''
83
+ );
63
84
  }
64
85
 
65
86
  /**
@@ -92,6 +113,8 @@ function resolveVersion(art, ex) {
92
113
 
93
114
  const CERTIFICATION_LEVELS = new Set(['BRONZE', 'SILVER', 'GOLD', 'PLATINUM']);
94
115
  const CERTIFICATION_STATUSES = new Set(['passed', 'not_passed', 'pending']);
116
+ /** Matches external-system.schema.json `certification.contractHash` pattern. */
117
+ const CONTRACT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
95
118
 
96
119
  /**
97
120
  * @param {string} raw
@@ -138,9 +161,10 @@ function resolveStatus(_art, ex) {
138
161
  }
139
162
 
140
163
  /**
141
- * Build `certification` object matching **external-system.schema.json** (required: enabled, publicKey, algorithm, issuer, version; optional status, level).
164
+ * Build `certification` object matching **external-system.schema.json** (required: enabled, publicKey, algorithm, issuer, version; optional status, level, publicKeyFingerprint, contractHash).
142
165
  * Fills gaps from `existingCertification` when the artifact omits publishable fields (common when dataplane redacts `publicKey`).
143
- * For **HS256** dev certificates with no PEM, uses a non-secret placeholder `publicKey` so the system file stays schema-valid.
166
+ * Dataplane issues **RS256** certificates with PEM `publicKey` and optional `publicKeyFingerprint` (sha256:… of SPKI); merge output uses **RS256** only.
167
+ * Optional **contractHash** is copied from the certificate `contractHash` or legacy `integrationHash` when it matches `sha256:` + 64 hex.
144
168
  *
145
169
  * @param {import('../api/types/certificates.types').CertificateArtifactResponse|null} artifact
146
170
  * @param {Object|null|undefined} existingCertification - Current `system.certification`
@@ -160,13 +184,13 @@ function buildCertificationFromArtifact(artifact, existingCertification) {
160
184
  const versionStr = resolveVersion(art, ex);
161
185
  if (!versionStr) return null;
162
186
 
163
- const algorithmUpper = trimOrEmpty(art.algorithm).toUpperCase();
164
- const algorithm = algorithmUpper === 'HS256' ? 'HS256' : 'RS256';
187
+ const publicKeyFingerprint = resolvePublicKeyFingerprint(art, ex);
188
+ const contractHash = resolveContractHash(art, ex);
165
189
 
166
190
  const out = {
167
191
  enabled: true,
168
192
  publicKey,
169
- algorithm,
193
+ algorithm: 'RS256',
170
194
  issuer,
171
195
  version: versionStr,
172
196
  status: resolveStatus(art, ex)
@@ -175,6 +199,12 @@ function buildCertificationFromArtifact(artifact, existingCertification) {
175
199
  if (level) {
176
200
  out.level = level;
177
201
  }
202
+ if (publicKeyFingerprint) {
203
+ out.publicKeyFingerprint = publicKeyFingerprint;
204
+ }
205
+ if (contractHash) {
206
+ out.contractHash = contractHash;
207
+ }
178
208
  return out;
179
209
  }
180
210
 
@@ -7,12 +7,12 @@
7
7
  "key":"external-system-schema",
8
8
  "name":"External System Configuration Schema",
9
9
  "description":"JSON schema for validating ExternalSystem configuration files",
10
- "version":"1.6.0",
10
+ "version":"1.6.2",
11
11
  "type":"schema",
12
12
  "category":"integration",
13
13
  "author":"AI Fabrix Team",
14
14
  "createdAt":"2024-01-01T00:00:00Z",
15
- "updatedAt":"2026-04-09T00:00:00Z",
15
+ "updatedAt":"2026-04-23T00:00:00Z",
16
16
  "compatibility":{
17
17
  "minVersion":"1.4.0",
18
18
  "maxVersion":"2.0.0",
@@ -29,6 +29,20 @@
29
29
 
30
30
  ],
31
31
  "changelog":[
32
+ {
33
+ "version":"1.6.2",
34
+ "date":"2026-04-23T00:00:00Z",
35
+ "changes":[
36
+ "certification.algorithm: RS256 only (removed HS256 from enum)"
37
+ ]
38
+ },
39
+ {
40
+ "version":"1.6.1",
41
+ "date":"2026-04-22T00:00:00Z",
42
+ "changes":[
43
+ "Optional certification.publicKeyFingerprint (sha256: hex of SPKI DER) for RS256 public key verification"
44
+ ]
45
+ },
32
46
  {
33
47
  "version":"1.6.0",
34
48
  "date":"2026-04-09T00:00:00Z",
@@ -679,15 +693,24 @@
679
693
  "publicKey":{
680
694
  "type":"string",
681
695
  "minLength":1,
682
- "description":"Public verification material (SPKI PEM for RS256; dev placeholder for HS256). Private keys must never appear in config."
696
+ "description":"Public verification material (SPKI PEM for RS256). Private keys must never appear in config."
697
+ },
698
+ "publicKeyFingerprint":{
699
+ "type":"string",
700
+ "pattern":"^sha256:[0-9a-f]{64}$",
701
+ "description":"SHA-256 fingerprint of the SubjectPublicKeyInfo DER (same convention as integration certificate publicKeyFingerprint); must match embedded publicKey."
702
+ },
703
+ "contractHash":{
704
+ "type":"string",
705
+ "pattern":"^sha256:[0-9a-f]{64}$",
706
+ "description":"Optional copy of the active integration certificate contractHash (certification contract material digest) for drift checks; should match the dataplane active trusted certificate when present."
683
707
  },
684
708
  "algorithm":{
685
709
  "type":"string",
686
710
  "enum":[
687
- "RS256",
688
- "HS256"
711
+ "RS256"
689
712
  ],
690
- "description":"Signing algorithm for certificate verification (RS256 in production; HS256 only for local dev when the dataplane uses the dev HMAC signer)."
713
+ "description":"Signing algorithm for integration certificate verification (RS256 only)."
691
714
  },
692
715
  "issuer":{
693
716
  "type":"string",
@@ -18,27 +18,16 @@ const { validateFieldReferences } = require('../datasource/field-reference-valid
18
18
  const { validateAbac } = require('../datasource/abac-validator');
19
19
 
20
20
  /**
21
- * Sets up AJV validator with external schemas
22
- * @async
23
- * @function setupAjvWithSchemas
24
- * @returns {Promise<Object>} AJV instance and schemas
21
+ * Load and normalize external-system + external-datasource schema objects for manifest AJV.
22
+ * @returns {{ externalSystemSchema: object, externalDatasourceSchema: object }}
25
23
  */
26
- async function setupAjvWithSchemas() {
27
- const ajv = new Ajv({
28
- allErrors: true,
29
- strict: false,
30
- removeAdditional: false
31
- });
32
- addFormats(ajv);
33
-
34
- // Load raw schema objects (not compiled validators)
24
+ function loadManifestExternalSchemas() {
35
25
  const externalSystemSchemaPath = path.join(__dirname, '..', 'schema', 'external-system.schema.json');
36
26
  const externalDatasourceSchemaPath = path.join(__dirname, '..', 'schema', 'external-datasource.schema.json');
37
27
 
38
28
  const externalSystemSchema = JSON.parse(fs.readFileSync(externalSystemSchemaPath, 'utf8'));
39
29
  let externalDatasourceSchema = JSON.parse(fs.readFileSync(externalDatasourceSchemaPath, 'utf8'));
40
30
 
41
- // Remove $schema for draft-2020-12 to avoid AJV issues
42
31
  if (externalDatasourceSchema.$schema && externalDatasourceSchema.$schema.includes('2020-12')) {
43
32
  const schemaCopy = { ...externalDatasourceSchema };
44
33
  delete schemaCopy.$schema;
@@ -52,7 +41,25 @@ async function setupAjvWithSchemas() {
52
41
  throw new Error('External datasource schema is missing required $id');
53
42
  }
54
43
 
55
- // external-datasource.schema.json references these by $id (aifabrix://schema/type/*)
44
+ return { externalSystemSchema, externalDatasourceSchema };
45
+ }
46
+
47
+ /**
48
+ * Sets up AJV validator with external schemas
49
+ * @async
50
+ * @function setupAjvWithSchemas
51
+ * @returns {Promise<Object>} AJV instance and schemas
52
+ */
53
+ async function setupAjvWithSchemas() {
54
+ const ajv = new Ajv({
55
+ allErrors: true,
56
+ strict: false,
57
+ removeAdditional: false
58
+ });
59
+ addFormats(ajv);
60
+
61
+ const { externalSystemSchema, externalDatasourceSchema } = loadManifestExternalSchemas();
62
+
56
63
  ajv.addSchema(require('../schema/type/document-storage.json'));
57
64
  ajv.addSchema(require('../schema/type/message-service.json'));
58
65
  ajv.addSchema(require('../schema/type/vector-store.json'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.44.2",
3
+ "version": "2.44.3",
4
4
  "description": "AI Fabrix Local Fabric & Deployment SDK",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -178,7 +178,8 @@ OPENTELEMETRY_ENDPOINT=
178
178
  # =============================================================================
179
179
  # Read by PemRsaCertificateSigner.from_environment in app/validation/certificates/signer.py.
180
180
  # When CERTIFICATE_PRIVATE_KEY and CERTIFICATE_PUBLIC_KEY are both set (non-empty PEM), the
181
- # engine uses RS256; otherwise it falls back to local HS256 (see build_certificate_signer in engine).
181
+ # engine uses RS256. When unset and LOCAL_MODE=true, a process-local ephemeral RSA keypair is used
182
+ # (still RS256, with publicKey + publicKeyFingerprint on issued certs). Non-local requires PEM keys.
182
183
  # PEM values are often multi-line; resolve via secret store / deploy pipeline (kv://) or inject as env.
183
184
  CERTIFICATE_PRIVATE_KEY=
184
185
  CERTIFICATE_PUBLIC_KEY=