taurusdb-core 0.5.0-rc.10 → 0.5.0-rc.2

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.
@@ -12,8 +12,7 @@ export function parseEngineFromDsnProtocol(protocol) {
12
12
  export function parseEnvProfile(env) {
13
13
  const dsn = asString(env.TAURUSDB_SQL_DSN);
14
14
  const explicitHost = asString(env.TAURUSDB_SQL_HOST);
15
- const explicitProfileName = asString(env.TAURUSDB_SQL_DATASOURCE);
16
- const profileName = explicitProfileName ?? "taurus_mcp";
15
+ const profileName = asString(env.TAURUSDB_SQL_DATASOURCE) ?? "taurus_mcp";
17
16
  let engine;
18
17
  let host;
19
18
  let port;
@@ -41,7 +40,6 @@ export function parseEnvProfile(env) {
41
40
  }
42
41
  if (!dsn &&
43
42
  !explicitHost &&
44
- !explicitProfileName &&
45
43
  !asString(env.TAURUSDB_SQL_USER) &&
46
44
  !asString(env.TAURUSDB_SQL_PASSWORD) &&
47
45
  !database) {
@@ -51,13 +49,23 @@ export function parseEnvProfile(env) {
51
49
  throw new Error("Failed to resolve SQL port from environment.");
52
50
  }
53
51
  username = username ?? asString(env.TAURUSDB_SQL_USER);
52
+ if (!username) {
53
+ throw new Error("Missing SQL username in environment. Set TAURUSDB_SQL_USER or include it in DSN.");
54
+ }
54
55
  passwordRef =
55
56
  passwordRef ??
56
57
  (Object.hasOwn(env, "TAURUSDB_SQL_PASSWORD")
57
58
  ? parseCredentialRef(env.TAURUSDB_SQL_PASSWORD, "TAURUSDB_SQL_PASSWORD")
58
59
  : undefined);
59
- if ((username && !passwordRef) || (!username && passwordRef)) {
60
- throw new Error("SQL credentials require both username and password. Omit both to use the local database login page.");
60
+ if (!passwordRef) {
61
+ throw new Error("Missing SQL password in environment. Set TAURUSDB_SQL_PASSWORD or include it in DSN.");
62
+ }
63
+ const mutationUsername = asString(env.TAURUSDB_SQL_MUTATION_USER);
64
+ const mutationPasswordRef = Object.hasOwn(env, "TAURUSDB_SQL_MUTATION_PASSWORD")
65
+ ? parseCredentialRef(env.TAURUSDB_SQL_MUTATION_PASSWORD, "TAURUSDB_SQL_MUTATION_PASSWORD")
66
+ : undefined;
67
+ if ((mutationUsername && !mutationPasswordRef) || (!mutationUsername && mutationPasswordRef)) {
68
+ throw new Error("Mutation credentials require both TAURUSDB_SQL_MUTATION_USER and TAURUSDB_SQL_MUTATION_PASSWORD.");
61
69
  }
62
70
  const poolSize = asInteger(env.TAURUSDB_SQL_POOL_SIZE);
63
71
  if (poolSize !== undefined && poolSize <= 0) {
@@ -69,7 +77,13 @@ export function parseEnvProfile(env) {
69
77
  host,
70
78
  port,
71
79
  database,
72
- user: username && passwordRef ? { username, password: passwordRef } : undefined,
80
+ user: {
81
+ username,
82
+ password: passwordRef,
83
+ },
84
+ mutationUser: mutationUsername && mutationPasswordRef
85
+ ? { username: mutationUsername, password: mutationPasswordRef }
86
+ : undefined,
73
87
  poolSize,
74
88
  });
75
89
  }
@@ -196,14 +196,19 @@ export function parseProfileRecord(name, value, context) {
196
196
  if (poolSize !== undefined && poolSize <= 0) {
197
197
  throw new Error(`Invalid datasource profile ${name} in ${context}: poolSize must be positive.`);
198
198
  }
199
- // `user` is the session credential. Keep older read-only aliases for compatibility.
199
+ // `user` is the read-only credential. Keep older read-only aliases for compatibility.
200
200
  const userRaw = value.user ??
201
201
  value.readonlyUser ??
202
202
  value.readonly ??
203
203
  value.readOnlyUser;
204
- const user = userRaw === undefined
204
+ if (userRaw === undefined) {
205
+ throw new Error(`Invalid datasource profile ${name} in ${context}: missing user.`);
206
+ }
207
+ const user = parseUserCredential(userRaw, `${context}.${name}.user`);
208
+ const mutationUserRaw = value.mutationUser ?? value.writeUser ?? value.rwUser;
209
+ const mutationUser = mutationUserRaw === undefined
205
210
  ? undefined
206
- : parseUserCredential(userRaw, `${context}.${name}.user`);
211
+ : parseUserCredential(mutationUserRaw, `${context}.${name}.mutationUser`);
207
212
  const tls = parseTlsOptions(value.tls, `${context}.${name}.tls`);
208
213
  return withRedactedToString({
209
214
  name,
@@ -214,6 +219,7 @@ export function parseProfileRecord(name, value, context) {
214
219
  instanceId,
215
220
  nodeId,
216
221
  user,
222
+ mutationUser,
217
223
  tls,
218
224
  poolSize,
219
225
  });
@@ -33,8 +33,10 @@ export interface DataSourceProfile {
33
33
  database?: string;
34
34
  instanceId?: string;
35
35
  nodeId?: string;
36
- /** Session credential used by guarded query tools and the human-gated recovery path. */
36
+ /** Read-only credential used by discovery, diagnostics, and query tools. */
37
37
  user?: UserCredential;
38
+ /** Separate credential required for mutation tools. No fallback is allowed. */
39
+ mutationUser?: UserCredential;
38
40
  tls?: TlsOptions;
39
41
  poolSize?: number;
40
42
  toString(): string;
@@ -160,13 +160,11 @@ export function buildRawConfigFromEnv(env) {
160
160
  maxFiles: parseInteger(env.TAURUSDB_MCP_AUDIT_MAX_FILES, "TAURUSDB_MCP_AUDIT_MAX_FILES"),
161
161
  },
162
162
  security: {
163
+ mutationsEnabled: parseBoolean(env.TAURUSDB_ENABLE_MUTATIONS, "TAURUSDB_ENABLE_MUTATIONS"),
163
164
  dynamicTargetsEnabled: parseBoolean(env.TAURUSDB_ENABLE_DYNAMIC_TARGETS, "TAURUSDB_ENABLE_DYNAMIC_TARGETS"),
164
- recycleBinRestoreEnabled: parseBoolean(env.TAURUSDB_ENABLE_RECYCLE_BIN_RESTORE, "TAURUSDB_ENABLE_RECYCLE_BIN_RESTORE"),
165
165
  requireTls: parseBoolean(env.TAURUSDB_REQUIRE_TLS, "TAURUSDB_REQUIRE_TLS"),
166
166
  approvalSecretPath: expandTildePath(readString(env.TAURUSDB_MUTATION_APPROVAL_SECRET_FILE)),
167
167
  approvalTtlSeconds: parseInteger(env.TAURUSDB_MUTATION_APPROVAL_TTL_SECONDS, "TAURUSDB_MUTATION_APPROVAL_TTL_SECONDS"),
168
- credentialIdleTtlMinutes: parseInteger(env.TAURUSDB_SQL_CREDENTIAL_IDLE_TTL_MINUTES, "TAURUSDB_SQL_CREDENTIAL_IDLE_TTL_MINUTES"),
169
- credentialMaxTtlMinutes: parseInteger(env.TAURUSDB_SQL_CREDENTIAL_MAX_TTL_MINUTES, "TAURUSDB_SQL_CREDENTIAL_MAX_TTL_MINUTES"),
170
168
  },
171
169
  slowSqlSource: {
172
170
  taurusApi: {
@@ -105,29 +105,23 @@ export declare const ConfigSchema: z.ZodObject<{
105
105
  maxFiles?: number | undefined;
106
106
  }>>;
107
107
  security: z.ZodDefault<z.ZodObject<{
108
+ mutationsEnabled: z.ZodDefault<z.ZodBoolean>;
108
109
  dynamicTargetsEnabled: z.ZodDefault<z.ZodBoolean>;
109
- recycleBinRestoreEnabled: z.ZodDefault<z.ZodBoolean>;
110
110
  requireTls: z.ZodDefault<z.ZodBoolean>;
111
111
  approvalSecretPath: z.ZodOptional<z.ZodString>;
112
112
  approvalTtlSeconds: z.ZodDefault<z.ZodNumber>;
113
- credentialIdleTtlMinutes: z.ZodDefault<z.ZodNumber>;
114
- credentialMaxTtlMinutes: z.ZodDefault<z.ZodNumber>;
115
113
  }, "strip", z.ZodTypeAny, {
114
+ mutationsEnabled: boolean;
116
115
  dynamicTargetsEnabled: boolean;
117
- recycleBinRestoreEnabled: boolean;
118
116
  requireTls: boolean;
119
117
  approvalTtlSeconds: number;
120
- credentialIdleTtlMinutes: number;
121
- credentialMaxTtlMinutes: number;
122
118
  approvalSecretPath?: string | undefined;
123
119
  }, {
120
+ mutationsEnabled?: boolean | undefined;
124
121
  dynamicTargetsEnabled?: boolean | undefined;
125
- recycleBinRestoreEnabled?: boolean | undefined;
126
122
  requireTls?: boolean | undefined;
127
123
  approvalSecretPath?: string | undefined;
128
124
  approvalTtlSeconds?: number | undefined;
129
- credentialIdleTtlMinutes?: number | undefined;
130
- credentialMaxTtlMinutes?: number | undefined;
131
125
  }>>;
132
126
  slowSqlSource: z.ZodDefault<z.ZodObject<{
133
127
  taurusApi: z.ZodDefault<z.ZodObject<{
@@ -364,12 +358,10 @@ export declare const ConfigSchema: z.ZodObject<{
364
358
  maxFiles: number;
365
359
  };
366
360
  security: {
361
+ mutationsEnabled: boolean;
367
362
  dynamicTargetsEnabled: boolean;
368
- recycleBinRestoreEnabled: boolean;
369
363
  requireTls: boolean;
370
364
  approvalTtlSeconds: number;
371
- credentialIdleTtlMinutes: number;
372
- credentialMaxTtlMinutes: number;
373
365
  approvalSecretPath?: string | undefined;
374
366
  };
375
367
  slowSqlSource: {
@@ -457,13 +449,11 @@ export declare const ConfigSchema: z.ZodObject<{
457
449
  maxFiles?: number | undefined;
458
450
  } | undefined;
459
451
  security?: {
452
+ mutationsEnabled?: boolean | undefined;
460
453
  dynamicTargetsEnabled?: boolean | undefined;
461
- recycleBinRestoreEnabled?: boolean | undefined;
462
454
  requireTls?: boolean | undefined;
463
455
  approvalSecretPath?: string | undefined;
464
456
  approvalTtlSeconds?: number | undefined;
465
- credentialIdleTtlMinutes?: number | undefined;
466
- credentialMaxTtlMinutes?: number | undefined;
467
457
  } | undefined;
468
458
  slowSqlSource?: {
469
459
  taurusApi?: {
@@ -22,13 +22,11 @@ const AuditSchema = z
22
22
  .default({});
23
23
  const SecuritySchema = z
24
24
  .object({
25
- dynamicTargetsEnabled: z.boolean().default(true),
26
- recycleBinRestoreEnabled: z.boolean().default(true),
27
- requireTls: z.boolean().default(false),
25
+ mutationsEnabled: z.boolean().default(false),
26
+ dynamicTargetsEnabled: z.boolean().default(false),
27
+ requireTls: z.boolean().default(true),
28
28
  approvalSecretPath: z.string().min(1).optional(),
29
29
  approvalTtlSeconds: z.number().int().positive().max(3600).default(300),
30
- credentialIdleTtlMinutes: z.number().int().positive().max(30).default(30),
31
- credentialMaxTtlMinutes: z.number().int().positive().max(480).default(480),
32
30
  })
33
31
  .default({});
34
32
  const CloudSchema = z
package/dist/engine.d.ts CHANGED
@@ -10,7 +10,7 @@ import { type ConfirmationStore, type ConfirmationRequest, type ConfirmationVali
10
10
  import { type Guardrail, type GuardrailDecision, type InspectInput } from "./safety/guardrail.js";
11
11
  import { type DatabaseInfo, type SchemaIntrospector, type TableInfo, type TableSchema } from "./schema/introspector.js";
12
12
  import { type FlashbackInput } from "./taurus/flashback.js";
13
- import type { RestoreRecycleBinTableInput } from "./taurus/recycle-bin.js";
13
+ import { type RestoreRecycleBinTableInput } from "./taurus/recycle-bin.js";
14
14
  import { type DbHotspotResult, type DiagnoseDbHotspotInput, type DiagnoseServiceLatencyInput, type FindTopSlowSqlInput, type FindTopSlowSqlResult, type DiagnoseConnectionSpikeInput, type DiagnoseLockContentionInput, type DiagnoseSlowQueryInput, type DiagnoseStoragePressureInput, type DiagnoseReplicationLagInput, type DiagnosticResult, type ServiceLatencyResult } from "./diagnostics/types.js";
15
15
  import { type SlowSqlSource } from "./diagnostics/slow-sql-source.js";
16
16
  import { type MetricsSource } from "./diagnostics/metrics-source.js";
@@ -65,6 +65,7 @@ export declare class TaurusDBEngine {
65
65
  explain(sql: string, ctx: SessionContext): Promise<ExplainResult>;
66
66
  explainEnhanced(sql: string, ctx: SessionContext): Promise<EnhancedExplainResult>;
67
67
  executeReadonly(sql: string, ctx: SessionContext, opts?: ReadonlyOptions): Promise<QueryResult>;
68
+ executeMutation(sql: string, ctx: SessionContext, opts?: MutationOptions): Promise<MutationResult>;
68
69
  flashbackQuery(input: FlashbackInput, ctx: SessionContext, opts?: ReadonlyOptions): Promise<QueryResult>;
69
70
  listRecycleBin(ctx: SessionContext, opts?: ReadonlyOptions): Promise<QueryResult>;
70
71
  restoreRecycleBinTable(input: RestoreRecycleBinTableInput, ctx: SessionContext, opts?: MutationOptions): Promise<MutationResult>;
package/dist/engine.js CHANGED
@@ -18,7 +18,7 @@ import { createSlowSqlSource, } from "./diagnostics/slow-sql-source.js";
18
18
  import { createMetricsSource, } from "./diagnostics/metrics-source.js";
19
19
  import { findLatestDeadlock, findMetadataLockWaits, findStatementDigestCandidatesForSqlHints, findStatementDigestSample, findStatementDigestSampleForSql, findStatementWaitEvents, findStorageStatementDigests, findTableStorageStats, findTopStatementDigests, isPerformanceSchemaEnabled, showLockWaits, showProcesslist, } from "./engine/data-access.js";
20
20
  import { diagnoseConnectionSpike, diagnoseDbHotspot, diagnoseLockContention, diagnoseServiceLatency, diagnoseSlowQuery, diagnoseStoragePressure, findTopSlowSql, } from "./engine/diagnostics.js";
21
- import { cancelQuery, close, executeReadonly, explain, explainEnhanced, flashbackQuery, getQueryStatus, handleConfirmation, issueConfirmation, listRecycleBin, restoreRecycleBinTable, validateConfirmation, } from "./engine/runtime.js";
21
+ import { cancelQuery, close, executeMutation, executeReadonly, explain, explainEnhanced, flashbackQuery, getQueryStatus, handleConfirmation, issueConfirmation, listRecycleBin, restoreRecycleBinTable, validateConfirmation, } from "./engine/runtime.js";
22
22
  async function readMutationApprovalSecret(filePath) {
23
23
  const info = await lstat(filePath);
24
24
  if (!info.isFile() || info.isSymbolicLink()) {
@@ -114,6 +114,9 @@ export class TaurusDBEngine {
114
114
  if (config.security.approvalSecretPath) {
115
115
  approvalSecret = await readMutationApprovalSecret(config.security.approvalSecretPath);
116
116
  }
117
+ if (config.security.mutationsEnabled && !approvalSecret && !options.confirmationStore) {
118
+ throw new Error("Mutations require TAURUSDB_MUTATION_APPROVAL_SECRET_FILE with at least 32 bytes.");
119
+ }
117
120
  const confirmationStore = options.confirmationStore ??
118
121
  createConfirmationStore({
119
122
  approvalSecret,
@@ -245,6 +248,9 @@ export class TaurusDBEngine {
245
248
  async executeReadonly(sql, ctx, opts) {
246
249
  return executeReadonly(this, sql, ctx, opts);
247
250
  }
251
+ async executeMutation(sql, ctx, opts) {
252
+ return executeMutation(this, sql, ctx, opts);
253
+ }
248
254
  async flashbackQuery(input, ctx, opts) {
249
255
  return flashbackQuery(this, input, ctx, opts);
250
256
  }
@@ -20,12 +20,12 @@ async function resolveTls(tls, secretResolver, host, requireTls) {
20
20
  if (requireTls && tls?.enabled === false) {
21
21
  throw new ConnectionPoolError("TLS is required by server policy and cannot be disabled for this datasource.");
22
22
  }
23
+ if (requireTls && tls?.rejectUnauthorized === false) {
24
+ throw new ConnectionPoolError("TLS certificate verification is required by server policy.");
25
+ }
23
26
  const resolved = {
24
27
  enabled: tls?.enabled ?? true,
25
- // Dynamic cloud targets are commonly bound by public IP while the server
26
- // certificate is issued to a DNS name. When TLS is explicitly enabled,
27
- // make CA/hostname verification a separate profile opt-in.
28
- rejectUnauthorized: tls?.rejectUnauthorized ?? false,
28
+ rejectUnauthorized: tls?.rejectUnauthorized ?? true,
29
29
  servername: tls?.servername ?? (isIP(host) === 0 ? host : undefined),
30
30
  };
31
31
  if (tls?.ca) {
@@ -39,10 +39,11 @@ async function resolveTls(tls, secretResolver, host, requireTls) {
39
39
  }
40
40
  return resolved;
41
41
  }
42
- function selectCredential(profile, _mode) {
43
- const credential = profile.user;
42
+ function selectCredential(profile, mode) {
43
+ const credential = mode === "rw" ? profile.mutationUser : profile.user;
44
44
  if (!credential) {
45
- throw new ConnectionPoolError(`Datasource "${profile.name}" has no active SQL credential session. Select a TaurusDB instance and open the returned local login URL, or call begin_sql_login.`);
45
+ const credentialKind = mode === "rw" ? "mutation" : "read-only";
46
+ throw new ConnectionPoolError(`Datasource "${profile.name}" does not define ${credentialKind} SQL credentials. Configure a dedicated ${credentialKind} database username and password reference.`);
46
47
  }
47
48
  return credential;
48
49
  }
package/dist/index.d.ts CHANGED
@@ -18,8 +18,6 @@ export type { CapabilitySnapshot, FeatureMatrix, FeatureStatus, KernelInfo, Taur
18
18
  export { createConfirmationStore, InMemoryConfirmationStore, parseApprovalRequest, signApprovalRequest, } from "./safety/confirmation-store.js";
19
19
  export type { ConfirmationStore, ConfirmationRequest, ConfirmationValidationResult, IssueInput, } from "./safety/confirmation-store.js";
20
20
  export { createGuardrail } from "./safety/guardrail.js";
21
- export { createSqlParser } from "./safety/parser/index.js";
22
- export type { SqlAst, StatementType as ParsedStatementType } from "./safety/parser/index.js";
23
21
  export type { Guardrail, GuardrailDecision, GuardrailRuntimeLimits, InspectInput, } from "./safety/guardrail.js";
24
22
  export type { ExplainRiskSummary, RiskLevel, ValidationResult } from "./safety/sql-validator.js";
25
23
  export { SchemaIntrospectionError } from "./schema/introspector.js";
package/dist/index.js CHANGED
@@ -10,7 +10,6 @@ export { createCapabilityProbe } from "./capability/probe.js";
10
10
  export { UnsupportedFeatureError } from "./capability/types.js";
11
11
  export { createConfirmationStore, InMemoryConfirmationStore, parseApprovalRequest, signApprovalRequest, } from "./safety/confirmation-store.js";
12
12
  export { createGuardrail } from "./safety/guardrail.js";
13
- export { createSqlParser } from "./safety/parser/index.js";
14
13
  export { SchemaIntrospectionError } from "./schema/introspector.js";
15
14
  export { ErrorCode, formatBlocked, formatConfirmationRequired, formatError, formatSuccess, } from "./utils/formatter.js";
16
15
  export { normalizeSql, sqlHash } from "./utils/hash.js";
@@ -54,7 +54,7 @@ export function validateToolScope(toolName, cls) {
54
54
  if (!READONLY_STATEMENTS.has(cls.statementType)) {
55
55
  return block(["T001"], [
56
56
  `Tool execute_readonly_sql only allows SELECT/SHOW/EXPLAIN/DESCRIBE, got ${cls.statementType}.`,
57
- "Use analyze_mutation_sql to generate non-executing SQL Advice.",
57
+ "Use execute_sql for controlled mutations.",
58
58
  ]);
59
59
  }
60
60
  return allow("low");
@@ -10,11 +10,6 @@ export declare const ErrorCode: {
10
10
  readonly QUERY_TIMEOUT: "QUERY_TIMEOUT";
11
11
  readonly QUERY_CANCELLED: "QUERY_CANCELLED";
12
12
  readonly CONNECTION_FAILED: "CONNECTION_FAILED";
13
- readonly DB_ENDPOINT_UNREACHABLE: "DB_ENDPOINT_UNREACHABLE";
14
- readonly DB_CONNECTION_REFUSED: "DB_CONNECTION_REFUSED";
15
- readonly DB_TLS_FAILED: "DB_TLS_FAILED";
16
- readonly DB_AUTH_FAILED: "DB_AUTH_FAILED";
17
- readonly DB_VALIDATION_TIMEOUT: "DB_VALIDATION_TIMEOUT";
18
13
  readonly RESULT_TOO_LARGE: "RESULT_TOO_LARGE";
19
14
  readonly AUDIT_FAILED: "AUDIT_FAILED";
20
15
  readonly SERVER_BUSY: "SERVER_BUSY";
@@ -9,11 +9,6 @@ export const ErrorCode = {
9
9
  QUERY_TIMEOUT: "QUERY_TIMEOUT",
10
10
  QUERY_CANCELLED: "QUERY_CANCELLED",
11
11
  CONNECTION_FAILED: "CONNECTION_FAILED",
12
- DB_ENDPOINT_UNREACHABLE: "DB_ENDPOINT_UNREACHABLE",
13
- DB_CONNECTION_REFUSED: "DB_CONNECTION_REFUSED",
14
- DB_TLS_FAILED: "DB_TLS_FAILED",
15
- DB_AUTH_FAILED: "DB_AUTH_FAILED",
16
- DB_VALIDATION_TIMEOUT: "DB_VALIDATION_TIMEOUT",
17
12
  RESULT_TOO_LARGE: "RESULT_TOO_LARGE",
18
13
  AUDIT_FAILED: "AUDIT_FAILED",
19
14
  SERVER_BUSY: "SERVER_BUSY",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taurusdb-core",
3
- "version": "0.5.0-rc.10",
3
+ "version": "0.5.0-rc.2",
4
4
  "description": "Shared TaurusDB data-plane engine for schema, SQL execution, guardrails, and diagnostics.",
5
5
  "license": "MIT",
6
6
  "repository": {