@senticor/hive 1.31.3 → 1.33.1

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 (2) hide show
  1. package/hive.cjs +137 -18
  2. package/package.json +1 -1
package/hive.cjs CHANGED
@@ -9014,22 +9014,22 @@ var artifactName = injectedString(
9014
9014
  true ? "hive-cli" : void 0
9015
9015
  ) || String(process.env.HIVE_CLI_ARTIFACT_NAME || "hive-cli").trim() || "hive-cli";
9016
9016
  var packageVersion = injectedString(
9017
- true ? "1.31.3" : void 0
9017
+ true ? "1.33.1" : void 0
9018
9018
  ) || String(process.env.HIVE_CLI_PACKAGE_VERSION || "").trim() || readPackageVersion();
9019
9019
  var releaseVersion = injectedString(
9020
- true ? "1.31.3" : void 0
9020
+ true ? "1.33.1" : void 0
9021
9021
  ) || String(process.env.HIVE_CLI_RELEASE_VERSION || "").trim() || packageVersion || "dev";
9022
9022
  var releaseChannel = injectedString(
9023
9023
  true ? "final" : void 0
9024
9024
  ) || String(process.env.HIVE_CLI_RELEASE_CHANNEL || "").trim() || (releaseVersion.includes("-rc.") ? "rc" : releaseVersion === "dev" ? "dev" : "final");
9025
9025
  var buildCommit = injectedString(
9026
- true ? "a4e10e16fc6a527e456f1cb68d5126166c7f9583" : void 0
9026
+ true ? "0bba73f7b6060454678d8f9f3763cabe75532ad4" : void 0
9027
9027
  ) || String(process.env.HIVE_CLI_BUILD_COMMIT || "").trim() || "unknown";
9028
9028
  var buildTimestamp = injectedString(
9029
- true ? "2026-09-06T22:05:05Z" : void 0
9029
+ true ? "2026-09-09T18:17:52Z" : void 0
9030
9030
  ) || String(process.env.HIVE_CLI_BUILD_TIMESTAMP || "").trim() || "unknown";
9031
9031
  var ociRef = injectedString(
9032
- true ? "registry.onstackit.cloud/senticor/hive-cli:1.31.3" : void 0
9032
+ true ? "registry.onstackit.cloud/senticor/hive-cli:1.33.1" : void 0
9033
9033
  ) || String(process.env.HIVE_CLI_OCI_REF || "").trim() || `registry.onstackit.cloud/senticor/${artifactName}:${releaseVersion}`;
9034
9034
  var RELEASE_INFO = Object.freeze({
9035
9035
  packageVersion,
@@ -24838,6 +24838,24 @@ function isValidCorsOrigin(value) {
24838
24838
  }
24839
24839
  var ROLE_KEY_RE = /^[a-z0-9][a-z0-9._:-]{0,63}$/;
24840
24840
 
24841
+ // ../hive-api-contracts/src/mission-kafka-ingest.ts
24842
+ init_cjs_shims();
24843
+ var MISSION_KAFKA_SASL_MECHANISMS = [
24844
+ "plain",
24845
+ "scram-sha-256",
24846
+ "scram-sha-512"
24847
+ ];
24848
+ var MISSION_KAFKA_TLS_CA_MAX_BYTES = 64 * 1024;
24849
+ function utf8ByteLength(value) {
24850
+ return new TextEncoder().encode(value).length;
24851
+ }
24852
+ function looksLikePemCertificate(value) {
24853
+ return value.includes("-----BEGIN CERTIFICATE-----") && value.includes("-----END CERTIFICATE-----");
24854
+ }
24855
+ function looksLikeJsonSchema(value) {
24856
+ return "$schema" in value || "type" in value || "$id" in value || "properties" in value;
24857
+ }
24858
+
24841
24859
  // src/admin/config.mts
24842
24860
  var TENANT_KINDS = ["customer", "organization"];
24843
24861
  var RoleKeySchema = external_exports.string().regex(ROLE_KEY_RE, "role key must be lower-case, 1-64 chars of [a-z0-9._:-]");
@@ -24899,9 +24917,25 @@ var KafkaSchema = external_exports.object({
24899
24917
  manifestTopic: external_exports.string().min(1).optional(),
24900
24918
  consumerGroup: external_exports.string().min(1).optional(),
24901
24919
  schemaSource: external_exports.enum(SCHEMA_SOURCES).default("inline"),
24920
+ // Round 8 (#83 review): buildKafkaPatchBody supported these since bms#257,
24921
+ // but the strict schema rejected the keys and the orchestrator never
24922
+ // forwarded them — the documented SCRAM enclave flow exited with
24923
+ // 'Unrecognized key: "saslMechanism"', and removing the key silently
24924
+ // provisioned PLAIN against a SCRAM-SHA-512 broker.
24925
+ saslMechanism: external_exports.enum(MISSION_KAFKA_SASL_MECHANISMS).optional(),
24926
+ // Round 9 (#83 review): trim before the min-length check — a whitespace-only
24927
+ // value otherwise passes local validation and 400s only at the Kafka PATCH,
24928
+ // after tenant/members/mission are already provisioned.
24929
+ fileNameHeaderKey: external_exports.string().trim().min(1).optional(),
24902
24930
  // Path (relative to the config file) to an inline JSON Schema. Required
24903
24931
  // when schemaSource === 'inline'.
24904
24932
  envelopeSchemaPath: external_exports.string().min(1).optional(),
24933
+ // #83 — path (relative to the config file) to the broker CA bundle in PEM
24934
+ // form, for private-CA/self-signed brokers (the normal enclave/Strimzi
24935
+ // setup). Resolved at build time and sent as `tls.caPem`; omit to trust
24936
+ // the default store. A path, not inline PEM: multi-line certificate
24937
+ // blocks do not belong in a JSON config.
24938
+ tlsCaPemPath: external_exports.string().min(1).optional(),
24905
24939
  schemaRegistry: SchemaRegistrySchema.optional(),
24906
24940
  classification: external_exports.string().min(1).optional(),
24907
24941
  sourceType: external_exports.string().min(1).optional()
@@ -25221,14 +25255,6 @@ async function ensureMission(client, mission2, opts) {
25221
25255
 
25222
25256
  // src/admin/steps/kafka.mts
25223
25257
  init_cjs_shims();
25224
-
25225
- // ../hive-api-contracts/src/mission-kafka-ingest.ts
25226
- init_cjs_shims();
25227
- function looksLikeJsonSchema(value) {
25228
- return "$schema" in value || "type" in value || "$id" in value || "properties" in value;
25229
- }
25230
-
25231
- // src/admin/steps/kafka.mts
25232
25258
  function buildKafkaPatchBody(input) {
25233
25259
  if (!input.apiKey || !input.apiSecret) {
25234
25260
  throw new Error(
@@ -25266,6 +25292,7 @@ function buildKafkaPatchBody(input) {
25266
25292
  if (input.fileNameHeaderKey) body.fileNameHeaderKey = input.fileNameHeaderKey;
25267
25293
  if (input.classification) body.classification = input.classification;
25268
25294
  if (input.sourceType) body.sourceType = input.sourceType;
25295
+ if (input.tlsCaPem) body.tls = { caPem: input.tlsCaPem };
25269
25296
  if (input.schemaSource === "inline") {
25270
25297
  body.envelopeSchema = input.envelopeSchema;
25271
25298
  } else {
@@ -25335,8 +25362,13 @@ async function runBootstrap(deps) {
25335
25362
  topic: config3.kafka.topic,
25336
25363
  manifestTopic: config3.kafka.manifestTopic,
25337
25364
  consumerGroup: config3.kafka.consumerGroup,
25365
+ // Round 8: forward the bms#257 fields — the schema accepted... nothing, and
25366
+ // this call silently dropped them, so SCRAM configs provisioned PLAIN.
25367
+ saslMechanism: config3.kafka.saslMechanism,
25368
+ fileNameHeaderKey: config3.kafka.fileNameHeaderKey,
25338
25369
  schemaSource: config3.kafka.schemaSource,
25339
25370
  envelopeSchema: deps.envelopeSchema,
25371
+ tlsCaPem: deps.tlsCaPem,
25340
25372
  schemaRegistry: config3.kafka.schemaRegistry ? {
25341
25373
  url: config3.kafka.schemaRegistry.url,
25342
25374
  apiKey: secrets.schemaRegistryApiKey,
@@ -25347,6 +25379,20 @@ async function runBootstrap(deps) {
25347
25379
  apiKey: secrets.kafkaApiKey ?? "",
25348
25380
  apiSecret: secrets.kafkaApiSecret ?? ""
25349
25381
  });
25382
+ if (deps.tlsCaPem) {
25383
+ const advertised = await advertisedTlsCaLimit(client);
25384
+ if (advertised == null) {
25385
+ throw new Error(
25386
+ "kafka.tlsCaPemPath is set, but this deployment does not advertise maxKafkaTlsCaPemBytes via GET /api/me/limits \u2014 it predates broker-CA support (#83) and would silently drop the CA from the binding. Upgrade the deployment, or remove tlsCaPemPath to trust the default store. Aborting BEFORE any mutation."
25387
+ );
25388
+ }
25389
+ const bytes = utf8ByteLength(deps.tlsCaPem);
25390
+ if (bytes > advertised) {
25391
+ throw new Error(
25392
+ `kafka.tlsCaPemPath: the CA bundle is ${bytes} UTF-8 bytes, but this deployment advertises maxKafkaTlsCaPemBytes=${advertised} via GET /api/me/limits \u2014 shrink the bundle or raise HIVE_MAX_KAFKA_TLS_CA_PEM_BYTES on the server.`
25393
+ );
25394
+ }
25395
+ }
25350
25396
  const state = readState(statePath);
25351
25397
  log(`Ensuring tenant ${displayKey} \u2026`);
25352
25398
  const tenant = await ensureTenant(client, config3.tenant);
@@ -25415,6 +25461,36 @@ async function runBootstrap(deps) {
25415
25461
  probe
25416
25462
  };
25417
25463
  }
25464
+ async function advertisedTlsCaLimit(client) {
25465
+ let res;
25466
+ try {
25467
+ res = await client.request("GET", "/api/me/limits");
25468
+ } catch (err) {
25469
+ throw new Error(
25470
+ `cannot reach GET /api/me/limits to check the deployment's CA size limit \u2014 aborting BEFORE any mutation (${err?.message ?? err})`
25471
+ );
25472
+ }
25473
+ if (!res.ok) {
25474
+ if (res.status === 404) return null;
25475
+ throw new Error(
25476
+ `GET /api/me/limits answered HTTP ${res.status} \u2014 cannot verify the deployment's CA size limit; aborting BEFORE any mutation`
25477
+ );
25478
+ }
25479
+ const body = res.json;
25480
+ if (body === null || typeof body !== "object") {
25481
+ throw new Error(
25482
+ "GET /api/me/limits returned an unparseable body \u2014 cannot verify the deployment's CA size limit; aborting BEFORE any mutation"
25483
+ );
25484
+ }
25485
+ const v = body.ingestLimits?.maxKafkaTlsCaPemBytes;
25486
+ if (v === void 0 || v === null) return null;
25487
+ if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
25488
+ throw new Error(
25489
+ `GET /api/me/limits advertises a malformed maxKafkaTlsCaPemBytes (${JSON.stringify(v)}) \u2014 cannot verify the deployment's CA size limit; aborting BEFORE any mutation`
25490
+ );
25491
+ }
25492
+ return v;
25493
+ }
25418
25494
 
25419
25495
  // src/admin/steps/inject.mts
25420
25496
  init_cjs_shims();
@@ -25463,6 +25539,7 @@ async function runConnector(client, missionId, tenant, config3) {
25463
25539
  // src/admin/cli-support.mts
25464
25540
  init_cjs_shims();
25465
25541
  var import_node_fs8 = require("fs");
25542
+ var import_node_crypto3 = require("crypto");
25466
25543
  var import_node_path7 = require("path");
25467
25544
  function loadConfigFile(configPath2, cwd = process.cwd()) {
25468
25545
  const abs = (0, import_node_path7.isAbsolute)(configPath2) ? configPath2 : (0, import_node_path7.resolve)(cwd, configPath2);
@@ -25480,6 +25557,46 @@ function resolveEnvelopeSchema(config3, configDir) {
25480
25557
  const abs = (0, import_node_path7.isAbsolute)(p) ? p : (0, import_node_path7.resolve)(configDir, p);
25481
25558
  return JSON.parse((0, import_node_fs8.readFileSync)(abs, "utf-8"));
25482
25559
  }
25560
+ function resolveTlsCaPem(config3, configDir) {
25561
+ const p = config3.kafka.tlsCaPemPath;
25562
+ if (!p) return void 0;
25563
+ const abs = (0, import_node_path7.isAbsolute)(p) ? p : (0, import_node_path7.resolve)(configDir, p);
25564
+ const pem = (0, import_node_fs8.readFileSync)(abs, "utf-8").trim();
25565
+ if (!looksLikePemCertificate(pem)) {
25566
+ throw new Error(
25567
+ `kafka.tlsCaPemPath (${p}) does not look like a PEM certificate bundle (missing BEGIN/END CERTIFICATE markers)`
25568
+ );
25569
+ }
25570
+ if (utf8ByteLength(pem) > MISSION_KAFKA_TLS_CA_MAX_BYTES) {
25571
+ throw new Error(
25572
+ `kafka.tlsCaPemPath (${p}) exceeds ${MISSION_KAFKA_TLS_CA_MAX_BYTES} UTF-8 bytes \u2014 a broker CA bundle should be a handful of certificates`
25573
+ );
25574
+ }
25575
+ for (const match of pem.matchAll(/-----BEGIN ([A-Z0-9 ]+)-----/g)) {
25576
+ if (match[1] !== "CERTIFICATE") {
25577
+ throw new Error(
25578
+ `kafka.tlsCaPemPath (${p}) contains a "${match[1]}" PEM block \u2014 only CERTIFICATE blocks belong in a CA bundle (if it is a private key, treat it as exposed and rotate it)`
25579
+ );
25580
+ }
25581
+ }
25582
+ const blocks = pem.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) ?? [];
25583
+ const beginCount = (pem.match(/-----BEGIN CERTIFICATE-----/g) ?? []).length;
25584
+ if (blocks.length === 0 || blocks.length !== beginCount) {
25585
+ throw new Error(
25586
+ `kafka.tlsCaPemPath (${p}) looks truncated \u2014 every certificate needs both its BEGIN and END marker`
25587
+ );
25588
+ }
25589
+ blocks.forEach((block, i) => {
25590
+ try {
25591
+ new import_node_crypto3.X509Certificate(block);
25592
+ } catch {
25593
+ throw new Error(
25594
+ `kafka.tlsCaPemPath (${p}): certificate block ${i + 1} of ${blocks.length} does not parse as X.509 \u2014 re-copy the bundle from its source (truncated or corrupted base64?)`
25595
+ );
25596
+ }
25597
+ });
25598
+ return pem;
25599
+ }
25483
25600
  function defaultStatePath(configDir, override, cwd = process.cwd()) {
25484
25601
  if (override) return (0, import_node_path7.isAbsolute)(override) ? override : (0, import_node_path7.resolve)(cwd, override);
25485
25602
  return (0, import_node_path7.resolve)(configDir, ".bootstrap-state.json");
@@ -25529,6 +25646,7 @@ async function cmdBootstrap(configPath2, envPath, flags) {
25529
25646
  const { config: config3, dir } = loadConfigFile(configPath2);
25530
25647
  loadEnvFile(envPath);
25531
25648
  const envelopeSchema = resolveEnvelopeSchema(config3, dir);
25649
+ const tlsCaPem = resolveTlsCaPem(config3, dir);
25532
25650
  const secrets = resolveSecrets(process.env);
25533
25651
  const dryRun = flags["dry-run"] === true;
25534
25652
  const client = dryRun ? createHttpClient(config3.apiUrl, "") : createHttpClient(config3.apiUrl, await acquireToken({ oidc: config3.oidc, secrets }));
@@ -25537,6 +25655,7 @@ async function cmdBootstrap(configPath2, envPath, flags) {
25537
25655
  config: config3,
25538
25656
  secrets,
25539
25657
  envelopeSchema,
25658
+ tlsCaPem,
25540
25659
  statePath: defaultStatePath(dir, flagStr(flags, "state")),
25541
25660
  options: { dryRun, force: flags.force === true, probe: flags["no-probe"] !== true },
25542
25661
  log: (m) => console.log(m)
@@ -26144,7 +26263,7 @@ var stack = defineCommand({
26144
26263
 
26145
26264
  // src/commands/diag.ts
26146
26265
  init_cjs_shims();
26147
- var import_node_crypto3 = __toESM(require("crypto"), 1);
26266
+ var import_node_crypto4 = __toESM(require("crypto"), 1);
26148
26267
  var import_node_os4 = __toESM(require("os"), 1);
26149
26268
  var import_node_fs10 = __toESM(require("fs"), 1);
26150
26269
  var import_node_path10 = __toESM(require("path"), 1);
@@ -26213,12 +26332,12 @@ function runDiagOffline(sub, verbose) {
26213
26332
  "cli"
26214
26333
  ];
26215
26334
  let totalSpec = 0;
26216
- const masterH = import_node_crypto3.default.createHash("sha256");
26335
+ const masterH = import_node_crypto4.default.createHash("sha256");
26217
26336
  for (const dir of specDirs) {
26218
26337
  const full = import_node_path10.default.join(specsBase, dir);
26219
26338
  if (!import_node_fs10.default.existsSync(full)) continue;
26220
26339
  const files = import_node_fs10.default.readdirSync(full).filter((f) => /\.(json|ya?ml)$/i.test(f)).sort();
26221
- const h = import_node_crypto3.default.createHash("sha256");
26340
+ const h = import_node_crypto4.default.createHash("sha256");
26222
26341
  for (const f of files) h.update(import_node_fs10.default.readFileSync(import_node_path10.default.join(full, f)));
26223
26342
  const hash2 = h.digest("hex").slice(0, 12);
26224
26343
  totalSpec += files.length;
@@ -27833,7 +27952,7 @@ var ajvFormatsModule2 = __toESM(require_dist(), 1);
27833
27952
 
27834
27953
  // src/lib/application-package.ts
27835
27954
  init_cjs_shims();
27836
- var import_node_crypto4 = require("crypto");
27955
+ var import_node_crypto5 = require("crypto");
27837
27956
  var import_node_fs12 = require("fs");
27838
27957
  var import_node_path11 = require("path");
27839
27958
  var ajvModule = __toESM(require_ajv(), 1);
@@ -27953,7 +28072,7 @@ var envelopeAjv = new AjvCtor({ allErrors: true, strict: false });
27953
28072
  addFormats(envelopeAjv);
27954
28073
  var validateStarterEnvelope = envelopeAjv.compile(DEFAULT_ENVELOPE_SCHEMA);
27955
28074
  function sha256Hex(bytes) {
27956
- return (0, import_node_crypto4.createHash)("sha256").update(bytes).digest("hex");
28075
+ return (0, import_node_crypto5.createHash)("sha256").update(bytes).digest("hex");
27957
28076
  }
27958
28077
  function formatBytes(n) {
27959
28078
  if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MiB`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@senticor/hive",
3
- "version": "1.31.3",
3
+ "version": "1.33.1",
4
4
  "description": "Cognitive Hive OS — unified CLI",
5
5
  "type": "commonjs",
6
6
  "bin": {