import net from "node:net"; import { ENVIRONMENT_DEV } from "../protocol/index.mjs"; export const DEV_DB_ENV_CONTRACT = Object.freeze({ contractVersion: "v1", environment: ENVIRONMENT_DEV, requiredEnv: Object.freeze([ "HWLAB_CLOUD_DB_URL", "HWLAB_CLOUD_DB_SSL_MODE" ]), secretRefs: Object.freeze([ Object.freeze({ env: "HWLAB_CLOUD_DB_URL", secretName: "hwlab-cloud-api-dev-db", secretKey: "database-url" }) ]), endpointAuthority: Object.freeze({ source: "secret-url-host", env: "HWLAB_CLOUD_DB_URL", requiredForReadiness: true, valueRedacted: true, endpointRedacted: true }), dns: Object.freeze({ source: "optional-public-dns-alias", serviceName: "cloud-api-db", namespace: "hwlab-dev", host: "cloud-api-db.hwlab-dev.svc.cluster.local", port: 5432, portName: "postgres", requiredForReadiness: false, usedForProbe: false }), readinessLayers: Object.freeze([ "dns", "tcp", "auth", "schema" ]), nonSecretDefaults: Object.freeze({ HWLAB_CLOUD_DB_SSL_MODE: "disable" }), forbidden: Object.freeze({ prodAllowed: false, secretPlaintextAllowed: false, liveDbEvidenceFromFixtures: false, invalidRuntimeHosts: Object.freeze([ ".invalid", "hwlab-dev-db.invalid" ]) }) }); const secretEnvNames = new Set(DEV_DB_ENV_CONTRACT.secretRefs.map((item) => item.env)); const supportedDbProtocols = new Set(["postgres:", "postgresql:"]); const defaultProbeTimeoutMs = 1200; export function buildDbHealthContract(env = process.env) { const fields = DEV_DB_ENV_CONTRACT.requiredEnv.map((name) => { const present = hasEnvValue(env, name); return { name, present, redacted: secretEnvNames.has(name), source: secretEnvNames.has(name) ? "k8s-secret-ref" : "runtime-env", required: true }; }); const missingEnv = fields.filter((field) => !field.present).map((field) => field.name); const configReady = missingEnv.length === 0; const connection = buildNotAttemptedConnection(configReady, missingEnv); const connected = false; const connectionChecked = false; const status = configReady ? "degraded" : "blocked"; const endpoint = buildEndpointDiagnostics(env); return { contractVersion: DEV_DB_ENV_CONTRACT.contractVersion, environment: DEV_DB_ENV_CONTRACT.environment, connected, liveConnected: connected, liveDbEvidence: connected, connectionChecked, connectionAttempted: connection.attempted, connectionResult: connection.result, configReady, ready: connected, status, mode: configReady ? "configured_without_live_connection_attempt" : "not_configured", fields, missingEnv, endpoint, endpointSource: endpoint.authoritative.source, optionalPublicDnsAlias: endpoint.optionalPublicDnsAlias, secretRefs: DEV_DB_ENV_CONTRACT.secretRefs.map(({ env: envName, secretName, secretKey }) => ({ env: envName, secretName, secretKey, present: hasEnvValue(env, envName), envInjected: hasEnvValue(env, envName), secretPresent: "not_observed_by_runtime", secretKeyPresent: "not_observed_by_runtime", redacted: true })), connection, redaction: buildRedactionSafety(connection), safety: { devOnly: true, prodAllowed: DEV_DB_ENV_CONTRACT.forbidden.prodAllowed, secretsRead: false, secretMaterialRead: false, valuesRedacted: true, endpointRedacted: true, liveDbEvidence: connected, liveDbConnectedEvidence: connected }, readinessLayers: buildReadinessLayers(connection), blocker: configReady ? "live DB connection has not been attempted by this health contract" : missingEnvBlocker(missingEnv), evidence: configReady ? "env_presence_only_no_live_db" : "env_contract_blocked" }; } export async function buildDbRuntimeReadiness(env = process.env, options = {}) { const base = buildDbHealthContract(env); if (!base.configReady) { return base; } if (probeDisabled(env, options)) { return { ...base, blocker: "live DB probe is disabled for this runtime", evidence: "env_presence_only_probe_disabled" }; } const connection = await probeDbConnection(env, options); return applyConnectionResult(base, connection); } export function buildDbEnvManifestPlaceholder() { return { requiredEnv: [...DEV_DB_ENV_CONTRACT.requiredEnv], secretRefs: DEV_DB_ENV_CONTRACT.secretRefs.map((item) => ({ ...item })), endpointAuthority: { ...DEV_DB_ENV_CONTRACT.endpointAuthority }, dns: { ...DEV_DB_ENV_CONTRACT.dns }, readinessLayers: [...DEV_DB_ENV_CONTRACT.readinessLayers], forbiddenRuntimeHosts: [...DEV_DB_ENV_CONTRACT.forbidden.invalidRuntimeHosts], nonSecretDefaults: { ...DEV_DB_ENV_CONTRACT.nonSecretDefaults }, fixtureEvidence: { liveDbEvidence: false, summary: "This manifest contract records only env and Secret reference names. It is not live DB evidence." } }; } export function summarizeDbContract(db = buildDbHealthContract()) { return { status: db.status, ready: db.ready, configReady: db.configReady, connected: db.connected, liveConnected: db.liveConnected ?? db.connected, connectionChecked: db.connectionChecked, connectionAttempted: db.connectionAttempted ?? db.connectionChecked, connectionResult: db.connectionResult ?? db.connection?.result ?? "unknown", liveDbEvidence: Boolean(db.liveDbEvidence ?? db.safety?.liveDbEvidence), requiredEnv: db.fields.map((field) => ({ name: field.name, present: field.present, redacted: field.redacted, source: field.source })), missingEnv: [...db.missingEnv], secretRefs: db.secretRefs.map( ({ env: envName, secretName, secretKey, present, envInjected, secretPresent, secretKeyPresent, redacted }) => ({ env: envName, secretName, secretKey, present, envInjected: envInjected ?? present, secretPresent: secretPresent ?? "not_observed", secretKeyPresent: secretKeyPresent ?? "not_observed", redacted }) ), endpointSource: db.endpointSource ?? db.endpoint?.authoritative?.source ?? db.connection?.endpointSource ?? DEV_DB_ENV_CONTRACT.endpointAuthority.source, optionalPublicDnsAlias: summarizeOptionalPublicDnsAlias(db.optionalPublicDnsAlias ?? db.endpoint?.optionalPublicDnsAlias), connection: summarizeConnection(db.connection), redaction: summarizeRedaction(db.redaction), readinessLayers: buildReadinessLayers(db.connection), blocker: db.blocker ?? null, fixtureEvidence: false }; } function hasEnvValue(env, name) { return typeof env?.[name] === "string" && env[name].trim().length > 0; } function probeDisabled(env, options) { return options.probe === false || env?.HWLAB_CLOUD_DB_PROBE_DISABLED === "1"; } function buildNotAttemptedConnection(configReady, missingEnv) { return { attempted: false, networkAttempted: false, result: configReady ? "not_attempted" : "not_attempted_missing_env", classification: configReady ? "configured_no_live_attempt" : "missing_runtime_env", endpointSource: DEV_DB_ENV_CONTRACT.endpointAuthority.source, probeType: "tcp-connect", endpointRedacted: true, valueRedacted: true, timeoutMs: null, durationMs: 0, errorCode: null, missingEnv: [...missingEnv] }; } function buildRedactionSafety(connection) { return { valuesRedacted: true, endpointRedacted: connection?.endpointRedacted !== false, valueRedacted: connection?.valueRedacted !== false, secretMaterialRead: false, secretRefsOnly: true }; } async function probeDbConnection(env, options) { const timeoutMs = normalizeTimeoutMs(options.timeoutMs ?? env?.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS); const parsed = parseDbUrlContract(env?.HWLAB_CLOUD_DB_URL); if (!parsed.ok) { return { attempted: true, networkAttempted: false, result: parsed.result, classification: parsed.classification, endpointSource: DEV_DB_ENV_CONTRACT.endpointAuthority.source, probeType: "tcp-connect", endpointRedacted: true, valueRedacted: true, timeoutMs, durationMs: 0, errorCode: parsed.errorCode, missingEnv: [] }; } return tcpConnect({ host: parsed.host, port: parsed.port, timeoutMs, endpointSource: DEV_DB_ENV_CONTRACT.endpointAuthority.source }); } export function parseDbUrlContract(rawUrl) { try { const url = new URL(rawUrl); if (!supportedDbProtocols.has(url.protocol)) { return { ok: false, result: "unsupported_protocol", classification: "db_url_unsupported_protocol", errorCode: "UNSUPPORTED_PROTOCOL" }; } if (!url.hostname) { return { ok: false, result: "invalid_url", classification: "db_url_missing_host", errorCode: "MISSING_HOST" }; } if (isForbiddenRuntimeHost(url.hostname)) { return { ok: false, result: "forbidden_runtime_host", classification: "db_url_forbidden_invalid_host", errorCode: "FORBIDDEN_INVALID_HOST" }; } const port = url.port ? Number.parseInt(url.port, 10) : 5432; if (!Number.isInteger(port) || port <= 0 || port > 65535) { return { ok: false, result: "invalid_url", classification: "db_url_invalid_port", errorCode: "INVALID_PORT" }; } return { ok: true, host: url.hostname, port }; } catch { return { ok: false, result: "invalid_url", classification: "db_url_parse_error", errorCode: "INVALID_URL" }; } } function tcpConnect({ host, port, timeoutMs, endpointSource }) { return new Promise((resolve) => { const startedAt = Date.now(); const socket = net.connect({ host, port, timeout: timeoutMs }); let settled = false; function finish(result) { if (settled) return; settled = true; socket.destroy(); resolve({ attempted: true, networkAttempted: true, endpointSource, probeType: "tcp-connect", endpointRedacted: true, valueRedacted: true, timeoutMs, durationMs: Date.now() - startedAt, missingEnv: [], ...result }); } socket.on("connect", () => { finish({ result: "connected", classification: "tcp_connected", errorCode: null }); }); socket.on("timeout", () => { finish({ result: "timeout", classification: "tcp_timeout", errorCode: "ETIMEDOUT" }); }); socket.on("error", (error) => { finish(classifySocketError(error)); }); }); } function classifySocketError(error) { const code = typeof error?.code === "string" ? error.code : "UNKNOWN"; if (code === "ECONNREFUSED") { return { result: "refused", classification: "tcp_refused", errorCode: code }; } if (code === "ENOTFOUND" || code === "EAI_AGAIN") { return { result: "dns_error", classification: "dns_resolution_failed", errorCode: code }; } if (code === "ETIMEDOUT") { return { result: "timeout", classification: "tcp_timeout", errorCode: code }; } if (code === "EHOSTUNREACH" || code === "ENETUNREACH") { return { result: "network_unreachable", classification: "network_unreachable", errorCode: code }; } return { result: "error", classification: "tcp_error", errorCode: code }; } function applyConnectionResult(base, connection) { const liveConnected = connection.result === "connected"; return { ...base, connected: liveConnected, liveConnected, liveDbEvidence: liveConnected, connectionChecked: connection.attempted, connectionAttempted: connection.attempted, connectionResult: connection.result, ready: liveConnected, status: liveConnected ? "ready" : "degraded", mode: liveConnected ? "live_connection_ready" : "live_connection_blocked", connection, endpoint: base.endpoint, endpointSource: connection.endpointSource ?? base.endpointSource, optionalPublicDnsAlias: base.optionalPublicDnsAlias, redaction: buildRedactionSafety(connection), safety: { ...base.safety, endpointRedacted: connection.endpointRedacted !== false, liveDbEvidence: liveConnected, liveDbConnectedEvidence: liveConnected }, readinessLayers: buildReadinessLayers(connection), blocker: liveConnected ? null : blockerForConnection(connection), evidence: liveConnected ? "live_db_tcp_connection_ready" : "live_db_tcp_connection_blocked" }; } function blockerForConnection(connection) { if (connection.result === "invalid_url") { return "DB URL is injected but cannot be parsed as a supported postgres URL"; } if (connection.result === "forbidden_runtime_host") { return "DB URL is injected but points at a forbidden .invalid DEV runtime host"; } if (connection.result === "unsupported_protocol") { return "DB URL is injected but does not use a supported postgres protocol"; } if (connection.result === "refused") { return "DB endpoint refused a TCP connection from the cloud-api runtime"; } if (connection.result === "timeout") { return "DB endpoint TCP connection timed out from the cloud-api runtime"; } if (connection.result === "dns_error") { return "DB endpoint DNS resolution failed from the cloud-api runtime"; } if (connection.result === "network_unreachable") { return "DB endpoint network is unreachable from the cloud-api runtime"; } return "DB endpoint TCP connection did not complete from the cloud-api runtime"; } function missingEnvBlocker(missingEnv) { return `DB runtime config is missing ${missingEnv.join(", ")}`; } function normalizeTimeoutMs(value) { const parsed = Number.parseInt(String(value ?? ""), 10); if (!Number.isInteger(parsed) || parsed <= 0) { return defaultProbeTimeoutMs; } return Math.min(parsed, 5000); } function isForbiddenRuntimeHost(hostname) { const normalized = String(hostname ?? "").toLowerCase(); return DEV_DB_ENV_CONTRACT.forbidden.invalidRuntimeHosts.some((host) => host.startsWith(".") ? normalized.endsWith(host) : normalized === host ); } function buildReadinessLayers(connection = {}) { const result = connection?.result ?? "unknown"; const connected = result === "connected"; const dnsReady = connected || ["refused", "timeout", "network_unreachable", "error"].includes(result); const tcpReady = connected; return { dns: { status: dnsReady ? "pass" : "not_proven", result }, tcp: { status: tcpReady ? "pass" : "not_proven", result }, auth: { status: "not_proven", result: connected ? "tcp_connected_auth_not_checked" : result }, schema: { status: "not_proven", result: connected ? "tcp_connected_schema_not_checked" : result } }; } function summarizeConnection(connection) { if (!connection) { return { attempted: false, result: "unknown", endpointRedacted: true, valueRedacted: true }; } return { attempted: Boolean(connection.attempted), networkAttempted: Boolean(connection.networkAttempted), result: connection.result, classification: connection.classification, endpointSource: connection.endpointSource ?? DEV_DB_ENV_CONTRACT.endpointAuthority.source, probeType: connection.probeType, endpointRedacted: true, valueRedacted: true, timeoutMs: connection.timeoutMs, durationMs: connection.durationMs, errorCode: connection.errorCode ?? null }; } function buildEndpointDiagnostics(env) { const authority = DEV_DB_ENV_CONTRACT.endpointAuthority; return { authoritative: { source: authority.source, env: authority.env, requiredForReadiness: authority.requiredForReadiness, usedForProbe: true, endpointRedacted: true, valueRedacted: true }, optionalPublicDnsAlias: buildOptionalPublicDnsAlias(env) }; } function buildOptionalPublicDnsAlias(env) { const alias = DEV_DB_ENV_CONTRACT.dns; const serviceNamePresent = env?.HWLAB_CLOUD_DB_SERVICE_NAME === alias.serviceName; const namespacePresent = env?.HWLAB_CLOUD_DB_SERVICE_NAMESPACE === alias.namespace; const hostPresent = env?.HWLAB_CLOUD_DB_HOST === alias.host; const portPresent = env?.HWLAB_CLOUD_DB_PORT === String(alias.port); const envPresent = serviceNamePresent || namespacePresent || hostPresent || portPresent; return { source: alias.source, serviceName: alias.serviceName, namespace: alias.namespace, port: alias.port, portName: alias.portName, requiredForReadiness: false, usedForProbe: false, envPresent, readyGate: "not_readiness_authority", note: envPresent ? "optional alias env observed; live DB readiness still follows the redacted Secret URL host" : "optional alias is not configured and is not required for live DB readiness" }; } function summarizeOptionalPublicDnsAlias(alias) { const fallback = buildOptionalPublicDnsAlias({}); const value = alias ?? fallback; return { source: value.source ?? fallback.source, serviceName: value.serviceName ?? fallback.serviceName, namespace: value.namespace ?? fallback.namespace, port: value.port ?? fallback.port, portName: value.portName ?? fallback.portName, requiredForReadiness: value.requiredForReadiness === true ? true : false, usedForProbe: value.usedForProbe === true ? true : false, envPresent: value.envPresent === true, readyGate: value.readyGate ?? "not_readiness_authority" }; } function summarizeRedaction(redaction) { return { valuesRedacted: redaction?.valuesRedacted !== false, endpointRedacted: redaction?.endpointRedacted !== false, valueRedacted: redaction?.valueRedacted !== false, secretMaterialRead: redaction?.secretMaterialRead === true, secretRefsOnly: redaction?.secretRefsOnly !== false }; }