@axiom-lattice/local-stores 3.2.4 → 3.2.6

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/dist/index.mjs CHANGED
@@ -1993,7 +1993,7 @@ function encryptPasswordInConfig(config) {
1993
1993
 
1994
1994
  // src/stores/LocalConnectionStore.ts
1995
1995
  import { v4 as uuid } from "uuid";
1996
- import { encryptConfigFields, decryptConfigFields } from "@axiom-lattice/core";
1996
+ import { encryptConfigFields, decryptConfigFields, encrypt as encrypt2, isEncryptedValue } from "@axiom-lattice/core";
1997
1997
  var DDL9 = `
1998
1998
  CREATE TABLE IF NOT EXISTS connection_configs (
1999
1999
  id TEXT NOT NULL,
@@ -2027,6 +2027,7 @@ var LocalConnectionStore = class {
2027
2027
  this.db = db;
2028
2028
  ensureTable(db, DDL9);
2029
2029
  this.ensureColumn("connection_configs", "secret_fields", "secret_fields TEXT");
2030
+ this.reencryptLegacySecrets();
2030
2031
  }
2031
2032
  ensureColumn(table, column, ddl2) {
2032
2033
  const cols = this.db.prepare(`PRAGMA table_info(${table})`).all();
@@ -2034,6 +2035,44 @@ var LocalConnectionStore = class {
2034
2035
  this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${ddl2}`);
2035
2036
  }
2036
2037
  }
2038
+ /**
2039
+ * Re-encrypt declared secret fields that are still stored as plaintext.
2040
+ *
2041
+ * A plugin can mark a field as a secret after connections already exist (e.g.
2042
+ * a field renamed from a plaintext key to a password), leaving legacy rows
2043
+ * with plaintext where ciphertext is now expected. This one-time, idempotent
2044
+ * pass encrypts any declared secret field that does not look like our
2045
+ * ciphertext; values that do look encrypted are left untouched, so genuine
2046
+ * ciphertext written under another key is never destroyed.
2047
+ */
2048
+ reencryptLegacySecrets() {
2049
+ const rows2 = this.db.prepare(
2050
+ `SELECT id, tenant_id, config, secret_fields FROM connection_configs WHERE secret_fields IS NOT NULL`
2051
+ ).all();
2052
+ for (const row of rows2) {
2053
+ const secretFields = parseSecretFields(row.secret_fields);
2054
+ if (!secretFields?.length) continue;
2055
+ let config;
2056
+ try {
2057
+ config = JSON.parse(row.config);
2058
+ } catch {
2059
+ continue;
2060
+ }
2061
+ let changed = false;
2062
+ for (const field of secretFields) {
2063
+ const value = config[field];
2064
+ if (typeof value === "string" && value.length > 0 && !isEncryptedValue(value)) {
2065
+ config[field] = encrypt2(value);
2066
+ changed = true;
2067
+ }
2068
+ }
2069
+ if (changed) {
2070
+ this.db.prepare(
2071
+ `UPDATE connection_configs SET config = ?, updated_at = ? WHERE tenant_id = ? AND id = ?`
2072
+ ).run(JSON.stringify(config), nowISO(), row.tenant_id, row.id);
2073
+ }
2074
+ }
2075
+ }
2037
2076
  async listByTenant(tenantId) {
2038
2077
  const rows2 = this.db.prepare(
2039
2078
  `SELECT * FROM connection_configs WHERE tenant_id = ? ORDER BY created_at`
@@ -2252,7 +2291,7 @@ function mapRowToEntry2(row) {
2252
2291
  }
2253
2292
 
2254
2293
  // src/stores/LocalMcpServerConfigStore.ts
2255
- import { encrypt as encrypt2, decrypt as decrypt2 } from "@axiom-lattice/core";
2294
+ import { encrypt as encrypt3, safeDecrypt } from "@axiom-lattice/core";
2256
2295
  var DDL11 = `
2257
2296
  CREATE TABLE IF NOT EXISTS lt_mcp_configs (
2258
2297
  id TEXT NOT NULL,
@@ -2399,28 +2438,18 @@ var LocalMcpServerConfigStore = class {
2399
2438
  function mapRowToEntry3(row) {
2400
2439
  const config = JSON.parse(row.config);
2401
2440
  if (config.env && row.is_env_encrypted) {
2402
- try {
2403
- const decryptedEnv = {};
2404
- for (const [key, value] of Object.entries(config.env)) {
2405
- decryptedEnv[key] = decrypt2(value);
2406
- }
2407
- config.env = decryptedEnv;
2408
- } catch (error) {
2409
- console.error("Failed to decrypt MCP server env:", error);
2410
- throw new Error("Failed to decrypt MCP server configuration");
2441
+ const decryptedEnv = {};
2442
+ for (const [key, value] of Object.entries(config.env)) {
2443
+ decryptedEnv[key] = safeDecrypt(value);
2411
2444
  }
2445
+ config.env = decryptedEnv;
2412
2446
  }
2413
2447
  if (config.headers && row.is_env_encrypted) {
2414
- try {
2415
- const decryptedHeaders = {};
2416
- for (const [key, value] of Object.entries(config.headers)) {
2417
- decryptedHeaders[key] = decrypt2(value);
2418
- }
2419
- config.headers = decryptedHeaders;
2420
- } catch (error) {
2421
- console.error("Failed to decrypt MCP server headers:", error);
2422
- throw new Error("Failed to decrypt MCP server configuration");
2448
+ const decryptedHeaders = {};
2449
+ for (const [key, value] of Object.entries(config.headers)) {
2450
+ decryptedHeaders[key] = safeDecrypt(value);
2423
2451
  }
2452
+ config.headers = decryptedHeaders;
2424
2453
  }
2425
2454
  return {
2426
2455
  id: row.id,
@@ -2442,7 +2471,7 @@ function encryptEnvInConfig(config) {
2442
2471
  if (configCopy.env && Object.keys(configCopy.env).length > 0) {
2443
2472
  const encryptedEnv = {};
2444
2473
  for (const [key, value] of Object.entries(configCopy.env)) {
2445
- encryptedEnv[key] = encrypt2(value);
2474
+ encryptedEnv[key] = encrypt3(value);
2446
2475
  }
2447
2476
  configCopy.env = encryptedEnv;
2448
2477
  isEnvEncrypted = true;
@@ -2450,7 +2479,7 @@ function encryptEnvInConfig(config) {
2450
2479
  if (configCopy.headers && Object.keys(configCopy.headers).length > 0) {
2451
2480
  const encryptedHeaders = {};
2452
2481
  for (const [key, value] of Object.entries(configCopy.headers)) {
2453
- encryptedHeaders[key] = encrypt2(value);
2482
+ encryptedHeaders[key] = encrypt3(value);
2454
2483
  }
2455
2484
  configCopy.headers = encryptedHeaders;
2456
2485
  isEnvEncrypted = true;
@@ -2459,7 +2488,7 @@ function encryptEnvInConfig(config) {
2459
2488
  }
2460
2489
 
2461
2490
  // src/stores/LocalModelProviderStore.ts
2462
- import { encrypt as encrypt3, decrypt as decrypt3 } from "@axiom-lattice/core";
2491
+ import { encrypt as encrypt4, safeDecrypt as safeDecrypt2 } from "@axiom-lattice/core";
2463
2492
  var DDL12 = `
2464
2493
  CREATE TABLE IF NOT EXISTS lt_model_providers (
2465
2494
  id TEXT NOT NULL,
@@ -2569,7 +2598,7 @@ var LocalModelProviderStore = class {
2569
2598
  // Phase 1 pins the LLM provider (ADR-138 decision 7b)
2570
2599
  "openai",
2571
2600
  data.baseURL,
2572
- data.apiKey ? encrypt3(data.apiKey) : null,
2601
+ data.apiKey ? encrypt4(data.apiKey) : null,
2573
2602
  data.apiKey ? data.apiKey.slice(-4) : null,
2574
2603
  data.enabled === false ? 0 : 1,
2575
2604
  now,
@@ -2604,7 +2633,7 @@ var LocalModelProviderStore = class {
2604
2633
  set("api_key_enc", null);
2605
2634
  set("api_key_hint", null);
2606
2635
  } else if (updates.apiKey) {
2607
- set("api_key_enc", encrypt3(updates.apiKey));
2636
+ set("api_key_enc", encrypt4(updates.apiKey));
2608
2637
  set("api_key_hint", updates.apiKey.slice(-4));
2609
2638
  }
2610
2639
  if (fields.length === 0) {
@@ -2736,12 +2765,7 @@ var LocalModelProviderStore = class {
2736
2765
  function mapRowToEntry4(row) {
2737
2766
  let apiKey;
2738
2767
  if (row.api_key_enc) {
2739
- try {
2740
- apiKey = decrypt3(row.api_key_enc);
2741
- } catch (error) {
2742
- console.error("Failed to decrypt model provider API key:", error);
2743
- throw new Error("Failed to decrypt model provider API key");
2744
- }
2768
+ apiKey = safeDecrypt2(row.api_key_enc);
2745
2769
  }
2746
2770
  return {
2747
2771
  id: row.id,
@@ -4206,7 +4230,7 @@ function mapRow6(row) {
4206
4230
  }
4207
4231
 
4208
4232
  // src/stores/LocalA2AApiKeyStore.ts
4209
- import { encrypt as encrypt4, decrypt as decrypt4 } from "@axiom-lattice/core";
4233
+ import { encrypt as encrypt5, decrypt as decrypt2, safeDecrypt as safeDecrypt3 } from "@axiom-lattice/core";
4210
4234
  import { randomUUID as randomUUID3 } from "crypto";
4211
4235
  var DDL17 = `
4212
4236
  CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
@@ -4277,7 +4301,7 @@ var LocalA2AApiKeyStore = class {
4277
4301
  ).all();
4278
4302
  for (const row of rows2) {
4279
4303
  try {
4280
- if (decrypt4(row.key_value) === key) return mapRowToRecord(row);
4304
+ if (decrypt2(row.key_value) === key) return mapRowToRecord(row);
4281
4305
  } catch {
4282
4306
  }
4283
4307
  }
@@ -4311,7 +4335,7 @@ var LocalA2AApiKeyStore = class {
4311
4335
  this.db.prepare(
4312
4336
  `INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, grants, label, created_at, updated_at)
4313
4337
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
4314
- ).run(id, encrypt4(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.grants ? JSON.stringify(input.grants) : null, input.label || null, now, now);
4338
+ ).run(id, encrypt5(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.grants ? JSON.stringify(input.grants) : null, input.label || null, now, now);
4315
4339
  const record2 = {
4316
4340
  id,
4317
4341
  key,
@@ -4380,7 +4404,7 @@ var LocalA2AApiKeyStore = class {
4380
4404
  const now = nowISO();
4381
4405
  this.db.prepare(
4382
4406
  `UPDATE lt_a2a_api_keys SET key_value = ?, updated_at = ? WHERE id = ?`
4383
- ).run(encrypt4(key), now, id);
4407
+ ).run(encrypt5(key), now, id);
4384
4408
  const row = this.db.prepare(`SELECT * FROM lt_a2a_api_keys WHERE id = ?`).get(id);
4385
4409
  if (!row) throw new Error(`A2A API key not found: ${id}`);
4386
4410
  const record2 = mapRowToRecord(row);
@@ -4397,7 +4421,7 @@ var LocalA2AApiKeyStore = class {
4397
4421
  const map2 = /* @__PURE__ */ new Map();
4398
4422
  for (const row of rows2) {
4399
4423
  try {
4400
- const key = decrypt4(row.key_value);
4424
+ const key = decrypt2(row.key_value);
4401
4425
  map2.set(key, {
4402
4426
  key,
4403
4427
  tenantId: row.tenant_id,
@@ -4411,15 +4435,9 @@ var LocalA2AApiKeyStore = class {
4411
4435
  }
4412
4436
  };
4413
4437
  function mapRowToRecord(row) {
4414
- let key = "";
4415
- try {
4416
- key = decrypt4(row.key_value);
4417
- } catch {
4418
- key = row.key_value;
4419
- }
4420
4438
  return {
4421
4439
  id: row.id,
4422
- key,
4440
+ key: safeDecrypt3(row.key_value),
4423
4441
  tenantId: row.tenant_id,
4424
4442
  projectId: row.project_id,
4425
4443
  assistantIds: parseAssistantIds(row.assistant_ids),