import express from "express"; import { createHash, randomUUID, timingSafeEqual } from "node:crypto"; import { createServer } from "node:http"; import { Pool } from "pg"; import { validateDataProductPublish, validateIntakeBatch } from "@nodedc/external-provider-contract/data-plane"; import { readConfig } from "./config.mjs"; import { loadDataProductDefinition, assertPublishMatchesDefinition, persistDataProductDefinition, persistDataProductPublish, pruneBatchReceipts, pruneDataProductHistory, prunePatchOutbox, readDataProductHistory, readDataProductSnapshot, readPatchEvents, } from "./data-product-delivery.mjs"; import { normalizeDataProductDefinition, safeDataProductDefinition } from "./data-product-policy.mjs"; import { reconcileDataProductDefinitions } from "./definitions.mjs"; import { assertBatchTimeBounds, rawRetentionExpiry } from "./intake-policy.mjs"; import { createManagedProvisionerRequestVerifier, ManagedProvisionerReplayCache, } from "./managed-provisioner-auth.mjs"; import { assertReaderProduct, consumerReaderBindingRequestHash, createReaderToken, hashReaderToken, normalizeManagedConsumerReaderBindingRequest, normalizeManagedConsumerReaderPlanRequest, normalizeManagedReaderBindingRequest, normalizeReaderBindingRequest, readerBindingRequestHash, safeManagedConsumerReaderBinding, safeReaderBinding, } from "./reader-binding.mjs"; import { resolveManagedReaderScope, resolveManagedReaderSourceConnection } from "./reader-source-scope.mjs"; import { migrate } from "./schema.mjs"; import { createWriterToken, canMigrateManagedWriterBindingToDurable, hashWriterToken, materializeDataProductPublish, materializeWriterBoundBatch, normalizeManagedWriterBindingRequest, normalizeWriterBindingRequest, safeWriterBinding, writerBindingRequestHash, } from "./writer-binding.mjs"; const config = readConfig(); const managedProvisionerReplayCache = config.managedProvisionerApiEnabled ? new ManagedProvisionerReplayCache({ maxEntries: config.managedProvisionerReplayCacheMaxEntries }) : null; const verifyManagedProvisionerRequest = config.managedProvisionerApiEnabled ? createManagedProvisionerRequestVerifier({ publicKey: config.managedProvisionerPublicKey, serviceId: config.managedProvisionerServiceId, keyId: config.managedProvisionerKeyId, audience: config.managedProvisionerAudience, maxSkewMs: config.managedProvisionerMaxSkewMs, replayCache: managedProvisionerReplayCache, }) : null; const foundryProvisionerReplayCache = config.foundryProvisionerApiEnabled ? new ManagedProvisionerReplayCache({ maxEntries: config.foundryProvisionerReplayCacheMaxEntries }) : null; const verifyFoundryProvisionerRequest = config.foundryProvisionerApiEnabled ? createManagedProvisionerRequestVerifier({ publicKey: config.foundryProvisionerPublicKey, serviceId: config.foundryProvisionerServiceId, keyId: config.foundryProvisionerKeyId, audience: config.foundryProvisionerAudience, maxSkewMs: config.foundryProvisionerMaxSkewMs, replayCache: foundryProvisionerReplayCache, }) : null; const pool = new Pool({ connectionString: config.databaseUrl, max: config.databasePoolSize }); const app = express(); const httpServer = createServer(app); let retentionSweepTimer = null; let lastRetentionSweepAt = null; const activeReaderStreams = new Map(); const activeStreamResponses = new Set(); let shuttingDown = false; let shutdownPromise = null; app.disable("x-powered-by"); app.use((req, _res, next) => { req.rawBody = Buffer.alloc(0); req.rawBodyCaptured = false; next(); }); app.use(express.json({ limit: config.maxBatchBytes, inflate: false, verify(req, _res, buffer) { req.rawBody = Buffer.from(buffer); req.rawBodyCaptured = true; }, })); app.get("/healthz", asyncRoute(async (_req, res) => { await pool.query("select 1"); res.json({ ok: true, service: "nodedc-external-data-plane", database: "ready", internalApiConfigured: Boolean(config.internalAccessToken), providerLogic: "absent", commandTransport: "absent", writerBindings: "supported", managedWriterBindings: "digest+idempotent-generation+explicit-revoke", readerBindings: "supported", managedReaderBindings: "digest+idempotent-generation+explicit-revoke", readerSourceScope: "writer-resolved+fail-closed-ambiguity", dataProductDelivery: "snapshot+history+durable-patch", legacyIntake: config.legacyIntakeEnabled ? "migration-only" : "disabled", legacyCapabilityProvisioning: config.provisionerApiEnabled ? "enabled" : "disabled", managedWriterBindingProvisioning: config.managedProvisionerApiEnabled ? "enabled" : "disabled", managedWriterBindingLifetime: config.managedProvisionerApiEnabled ? "explicit-revoke" : "disabled", managedReaderBindingProvisioning: config.managedProvisionerApiEnabled ? "enabled" : "disabled", managedReaderBindingLifetime: config.managedProvisionerApiEnabled ? "explicit-revoke" : "disabled", foundryReaderBindingProvisioning: config.foundryProvisionerApiEnabled ? "digest+server-resolved-source" : "disabled", foundryReaderBindingLifetime: config.foundryProvisionerApiEnabled ? "explicit-revoke" : "disabled", rawRetentionSweep: { mode: "server-scheduled", lastSweepAt: lastRetentionSweepAt, }, }); })); app.put("/internal/data-plane/v1/data-products/:dataProductId", requireProvisionerApi, asyncRoute(async (req, res) => { const definition = normalizeDataProductDefinition({ ...req.body, id: req.params.dataProductId }); const saved = await persistDataProductDefinition(pool, definition); res.json({ ok: true, dataProduct: safeDataProductDefinition(saved) }); })); app.post("/internal/data-plane/v1/data-products/:dataProductId/publish", requireWriterBinding, asyncRoute(async (req, res) => { if (hasScopeHeaders(req)) throw httpError(400, "data_product_publish_scope_headers_forbidden"); const validation = validateDataProductPublish(req.body, { maxFacts: config.maxFactsPerPublish, maxAttributesBytes: config.maxAttributesBytesPerFact, }); if (!validation.ok) throw httpError(422, validation.errors[0] || "invalid_data_product_publish"); const dataProductId = requireIdentifier(req.params.dataProductId, "data_product_id_invalid"); const definition = await loadDataProductDefinition(pool, dataProductId); if (!definition) throw httpError(404, "data_product_not_found"); const batch = materializeDataProductPublish(req.body, req.writerBinding, definition, dataProductId); const canonicalValidation = validateIntakeBatch(batch); if (!canonicalValidation.ok) throw httpError(422, "materialized_publish_invalid"); assertBatchTimeBounds(batch, { maxFutureSkewSeconds: config.maxFutureSkewSeconds }); const receipt = await persistDataProductPublish(pool, batch, definition, { maxPatchOperations: config.maxPatchOperations, maxPatchBytes: config.maxPatchBytes, }); res.status(receipt.idempotent ? 200 : 201).json({ ok: true, ...receipt }); })); app.get("/internal/data-plane/v1/writer/data-products", requireWriterBinding, asyncRoute(async (req, res) => { const dataProducts = await listGrantedProducts(req.writerBinding); res.json({ ok: true, dataProducts }); })); app.get("/internal/data-plane/v1/reader/data-products", requireReaderBinding, asyncRoute(async (req, res) => { const dataProducts = await listGrantedProducts(req.readerBinding); res.json({ ok: true, dataProducts }); })); app.post("/internal/data-plane/v1/intake", requireLegacyIntake, requireInternalApi, asyncRoute(async (req, res) => { const validation = validateIntakeBatch(req.body); if (!validation.ok) throw httpError(422, "invalid_intake_batch"); assertBatchTimeBounds(req.body, { maxFutureSkewSeconds: config.maxFutureSkewSeconds }); await assertLegacyBatchProduct(req.body); const scope = requireScope(req); if (scope.tenantId !== req.body.source.tenantId || scope.connectionId !== req.body.source.connectionId) { throw httpError(403, "scope_mismatch"); } const result = await persistBatch(req.body); res.status(result.idempotent ? 200 : 201).json({ ok: true, ...result }); })); app.post("/internal/data-plane/v1/intake/writer-bound", requireLegacyIntake, requireWriterBinding, asyncRoute(async (req, res) => { const batch = materializeWriterBoundBatch(req.body, req.writerBinding, { hasScopeHeaders: hasScopeHeaders(req), }); const validation = validateIntakeBatch(batch); if (!validation.ok) throw httpError(422, "invalid_intake_batch"); assertBatchTimeBounds(batch, { maxFutureSkewSeconds: config.maxFutureSkewSeconds }); await assertLegacyBatchProduct(batch); const result = await persistBatch(batch); res.status(result.idempotent ? 200 : 201).json({ ok: true, ...result }); })); app.put("/internal/data-plane/v1/writer-bindings/by-key/:bindingKey", requireManagedProvisionerApi, asyncRoute(async (req, res) => { const bindingKey = requireIdentifier(req.params.bindingKey, "managed_writer_binding_key_invalid"); const policy = normalizeManagedWriterBindingRequest(req.body); await assertRegisteredProductIds(policy.allowedDataProductIds); const requestHash = writerBindingRequestHash(policy); const client = await pool.connect(); let response; try { await client.query("begin"); await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [bindingKey]); const existing = await client.query( `select id, token_hash as "capabilityDigest", binding_key as "bindingKey", request_hash as "requestHash", generation, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_writer_bindings where binding_key = $1 and generation = $2 for update`, [bindingKey, policy.generation], ); if (existing.rowCount) { const binding = existing.rows[0]; if (binding.requestHash !== requestHash) { const legacyCanMigrate = canMigrateManagedWriterBindingToDurable(binding, policy); if (!legacyCanMigrate) throw httpError(409, "managed_writer_binding_request_conflict"); const migrated = await client.query( `update external_data_plane_writer_bindings set request_hash = $3, expires_at = null, rotated_at = now() where binding_key = $1 and generation = $2 and token_hash = $4 and active = true and expires_at > now() returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingKey, policy.generation, requestHash, policy.capabilityDigest], ); if (!migrated.rowCount) throw httpError(409, "managed_writer_binding_generation_inactive"); response = { status: 200, idempotent: false, migrated: true, binding: migrated.rows[0] }; } else { if (binding.active !== true || binding.expiresAt !== null) { throw httpError(409, "managed_writer_binding_generation_inactive"); } response = { status: 200, idempotent: true, migrated: false, binding }; } } else { const inserted = await client.query( `insert into external_data_plane_writer_bindings ( id, token_hash, binding_key, request_hash, generation, tenant_id, connection_id, provider_id, allowed_data_product_ids, expires_at ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10) returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [ randomUUID(), policy.capabilityDigest, bindingKey, requestHash, policy.generation, policy.tenantId, policy.connectionId, policy.providerId, JSON.stringify(policy.allowedDataProductIds), policy.expiresAt, ], ); response = { status: 201, idempotent: false, binding: inserted.rows[0] }; } await client.query("commit"); } catch (error) { await client.query("rollback"); if (error?.code === "23505") { throw httpError(409, "managed_writer_binding_capability_digest_conflict"); } throw error; } finally { client.release(); } res.set("Cache-Control", "no-store, max-age=0"); res.status(response.status).json({ ok: true, idempotent: response.idempotent, migrated: response.migrated === true, writerBinding: safeWriterBinding(response.binding), }); })); app.post("/internal/data-plane/v1/writer-bindings/by-key/:bindingKey/generations/:generation/revoke", requireManagedProvisionerApi, asyncRoute(async (req, res) => { const bindingKey = requireIdentifier(req.params.bindingKey, "managed_writer_binding_key_invalid"); const generation = requirePositiveInteger(req.params.generation, "managed_writer_binding_generation_invalid"); const client = await pool.connect(); let binding; let idempotent; try { await client.query("begin"); await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [bindingKey]); const existing = await client.query( `select id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_writer_bindings where binding_key = $1 and generation = $2 for update`, [bindingKey, generation], ); if (!existing.rowCount) throw httpError(404, "managed_writer_binding_not_found"); if (existing.rows[0].active === true) { const revoked = await client.query( `update external_data_plane_writer_bindings set active = false, revoked_at = now() where binding_key = $1 and generation = $2 and active = true returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingKey, generation], ); binding = revoked.rows[0]; idempotent = false; } else { binding = existing.rows[0]; idempotent = true; } await client.query("commit"); } catch (error) { await client.query("rollback"); throw error; } finally { client.release(); } res.set("Cache-Control", "no-store, max-age=0"); res.json({ ok: true, idempotent, writerBinding: safeWriterBinding(binding) }); })); app.post("/internal/data-plane/v1/writer-bindings", requireProvisionerApi, asyncRoute(async (req, res) => { const policy = normalizeWriterBindingRequest(req.body, { maxTtlDays: config.writerBindingMaxTtlDays, }); await assertRegisteredProductIds(policy.allowedDataProductIds); const token = createWriterToken(); const bindingId = randomUUID(); const result = await pool.query( `insert into external_data_plane_writer_bindings ( id, token_hash, tenant_id, connection_id, provider_id, allowed_data_product_ids, expires_at ) values ($1, $2, $3, $4, $5, $6::jsonb, $7) returning id, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [ bindingId, hashWriterToken(token), policy.tenantId, policy.connectionId, policy.providerId, JSON.stringify(policy.allowedDataProductIds), policy.expiresAt, ], ); // The token is intentionally returned exactly once to a trusted provisioner. // It must be placed directly into an opaque Engine credential reference and // must never be logged, stored in L2 graph data or shown to a consumer. sendOneTimeCapability(res, 201, { ok: true, writerBinding: safeWriterBinding(result.rows[0]), token }); })); app.post("/internal/data-plane/v1/writer-bindings/:bindingId/rotate", requireProvisionerApi, asyncRoute(async (req, res) => { const bindingId = requireUuid(req.params.bindingId, "writer_binding_id_invalid"); const token = createWriterToken(); const result = await pool.query( `update external_data_plane_writer_bindings set token_hash = $2, rotated_at = now() where id = $1 and binding_key is null and active = true and expires_at > now() returning id, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingId, hashWriterToken(token)], ); if (!result.rowCount) throw httpError(404, "writer_binding_not_found_or_inactive"); sendOneTimeCapability(res, 200, { ok: true, writerBinding: safeWriterBinding(result.rows[0]), token }); })); app.post("/internal/data-plane/v1/writer-bindings/:bindingId/revoke", requireProvisionerApi, asyncRoute(async (req, res) => { const bindingId = requireUuid(req.params.bindingId, "writer_binding_id_invalid"); const result = await pool.query( `update external_data_plane_writer_bindings set active = false, revoked_at = now() where id = $1 and binding_key is null and active = true returning id, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingId], ); if (!result.rowCount) throw httpError(404, "writer_binding_not_found_or_inactive"); res.json({ ok: true, writerBinding: safeWriterBinding(result.rows[0]) }); })); app.post("/internal/data-plane/v1/consumer-reader-bindings/plan", requireFoundryProvisionerApi, asyncRoute(async (req, res) => { const policy = normalizeManagedConsumerReaderPlanRequest(req.body); await assertRegisteredProductIds(policy.allowedDataProductIds); await resolveManagedReaderScope(pool, policy.allowedDataProductIds); const dataProducts = []; for (const dataProductId of policy.allowedDataProductIds) { const definition = await loadDataProductDefinition(pool, dataProductId); if (!definition || definition.active === false) throw httpError(404, "data_product_not_found"); dataProducts.push(safeDataProductDefinition(definition)); } res.set("Cache-Control", "no-store, max-age=0"); res.json({ ok: true, sourceScope: "resolved-server-side", dataProducts }); })); app.put("/internal/data-plane/v1/consumer-reader-bindings/by-key/:bindingKey", requireFoundryProvisionerApi, asyncRoute(async (req, res) => { const bindingKey = requireIdentifier(req.params.bindingKey, "managed_consumer_reader_binding_key_invalid"); const policy = normalizeManagedConsumerReaderBindingRequest(req.body); await assertRegisteredProductIds(policy.allowedDataProductIds); const requestHash = consumerReaderBindingRequestHash(policy); const client = await pool.connect(); let response; try { await client.query("begin"); await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [bindingKey]); const existing = await client.query( `select id, binding_key as "bindingKey", request_hash as "requestHash", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_reader_bindings where binding_key = $1 and generation = $2 for update`, [bindingKey, policy.generation], ); if (existing.rowCount) { const binding = existing.rows[0]; if (binding.requestHash !== requestHash) throw httpError(409, "managed_consumer_reader_binding_request_conflict"); if (binding.active !== true || binding.expiresAt !== null) { throw httpError(409, "managed_consumer_reader_binding_generation_inactive"); } response = { status: 200, idempotent: true, binding }; } else { const source = await resolveManagedReaderScope(client, policy.allowedDataProductIds); const inserted = await client.query( `insert into external_data_plane_reader_bindings ( id, token_hash, binding_key, request_hash, generation, tenant_id, connection_id, source_connection_id, provider_id, allowed_data_product_ids, expires_at ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11) returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [ randomUUID(), policy.capabilityDigest, bindingKey, requestHash, policy.generation, source.tenantId, source.connectionId, source.connectionId, source.providerId, JSON.stringify(policy.allowedDataProductIds), policy.expiresAt, ], ); response = { status: 201, idempotent: false, binding: inserted.rows[0] }; } await client.query("commit"); } catch (error) { await client.query("rollback"); if (error?.code === "23505") throw httpError(409, "managed_consumer_reader_capability_digest_conflict"); throw error; } finally { client.release(); } res.set("Cache-Control", "no-store, max-age=0"); res.status(response.status).json({ ok: true, idempotent: response.idempotent, readerBinding: safeManagedConsumerReaderBinding(response.binding), }); })); app.post("/internal/data-plane/v1/consumer-reader-bindings/by-key/:bindingKey/generations/:generation/revoke", requireFoundryProvisionerApi, asyncRoute(async (req, res) => { const bindingKey = requireIdentifier(req.params.bindingKey, "managed_consumer_reader_binding_key_invalid"); const generation = requirePositiveInteger(req.params.generation, "managed_consumer_reader_generation_invalid"); const client = await pool.connect(); let binding; let idempotent; try { await client.query("begin"); await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [bindingKey]); const existing = await client.query( `select id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_reader_bindings where binding_key = $1 and generation = $2 for update`, [bindingKey, generation], ); if (!existing.rowCount) throw httpError(404, "managed_consumer_reader_binding_not_found"); if (existing.rows[0].active === true) { const revoked = await client.query( `update external_data_plane_reader_bindings set active = false, revoked_at = now() where binding_key = $1 and generation = $2 and active = true returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingKey, generation], ); binding = revoked.rows[0]; idempotent = false; } else { binding = existing.rows[0]; idempotent = true; } await client.query("commit"); } catch (error) { await client.query("rollback"); throw error; } finally { client.release(); } res.set("Cache-Control", "no-store, max-age=0"); res.json({ ok: true, idempotent, readerBinding: safeManagedConsumerReaderBinding(binding) }); })); app.put("/internal/data-plane/v1/reader-bindings/by-key/:bindingKey", requireManagedProvisionerApi, asyncRoute(async (req, res) => { const bindingKey = requireIdentifier(req.params.bindingKey, "managed_reader_binding_key_invalid"); const policy = normalizeManagedReaderBindingRequest(req.body); await assertRegisteredProductIds(policy.allowedDataProductIds); const requestHash = readerBindingRequestHash(policy); const client = await pool.connect(); let response; try { await client.query("begin"); await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [bindingKey]); const existing = await client.query( `select id, binding_key as "bindingKey", request_hash as "requestHash", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_reader_bindings where binding_key = $1 and generation = $2 for update`, [bindingKey, policy.generation], ); if (existing.rowCount) { let binding = existing.rows[0]; if (binding.requestHash !== requestHash) { throw httpError(409, "managed_reader_binding_request_conflict"); } if (binding.active !== true || binding.expiresAt !== null) { throw httpError(409, "managed_reader_binding_generation_inactive"); } if (!binding.sourceConnectionId) { const sourceConnectionId = await resolveManagedReaderSourceConnection(client, policy); const resolved = await client.query( `update external_data_plane_reader_bindings set source_connection_id = $3 where binding_key = $1 and generation = $2 and source_connection_id is null returning id, binding_key as "bindingKey", request_hash as "requestHash", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingKey, policy.generation, sourceConnectionId], ); binding = resolved.rows[0] || binding; } response = { status: 200, idempotent: true, binding }; } else { const sourceConnectionId = await resolveManagedReaderSourceConnection(client, policy); const inserted = await client.query( `insert into external_data_plane_reader_bindings ( id, token_hash, binding_key, request_hash, generation, tenant_id, connection_id, source_connection_id, provider_id, allowed_data_product_ids, expires_at ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, $11) returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [ randomUUID(), policy.capabilityDigest, bindingKey, requestHash, policy.generation, policy.tenantId, policy.connectionId, sourceConnectionId, policy.providerId, JSON.stringify(policy.allowedDataProductIds), policy.expiresAt, ], ); response = { status: 201, idempotent: false, binding: inserted.rows[0] }; } await client.query("commit"); } catch (error) { await client.query("rollback"); if (error?.code === "23505") { throw httpError(409, "managed_reader_binding_capability_digest_conflict"); } throw error; } finally { client.release(); } res.set("Cache-Control", "no-store, max-age=0"); res.status(response.status).json({ ok: true, idempotent: response.idempotent, readerBinding: safeReaderBinding(response.binding), }); })); app.post("/internal/data-plane/v1/reader-bindings/by-key/:bindingKey/generations/:generation/revoke", requireManagedProvisionerApi, asyncRoute(async (req, res) => { const bindingKey = requireIdentifier(req.params.bindingKey, "managed_reader_binding_key_invalid"); const generation = requirePositiveInteger(req.params.generation, "managed_reader_binding_generation_invalid"); const client = await pool.connect(); let binding; let idempotent; try { await client.query("begin"); await client.query("select pg_advisory_xact_lock(hashtextextended($1, 0))", [bindingKey]); const existing = await client.query( `select id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_reader_bindings where binding_key = $1 and generation = $2 for update`, [bindingKey, generation], ); if (!existing.rowCount) throw httpError(404, "managed_reader_binding_not_found"); if (existing.rows[0].active === true) { const revoked = await client.query( `update external_data_plane_reader_bindings set active = false, revoked_at = now() where binding_key = $1 and generation = $2 and active = true returning id, binding_key as "bindingKey", generation, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingKey, generation], ); binding = revoked.rows[0]; idempotent = false; } else { binding = existing.rows[0]; idempotent = true; } await client.query("commit"); } catch (error) { await client.query("rollback"); throw error; } finally { client.release(); } res.set("Cache-Control", "no-store, max-age=0"); res.json({ ok: true, idempotent, readerBinding: safeReaderBinding(binding) }); })); app.post("/internal/data-plane/v1/reader-bindings", requireProvisionerApi, asyncRoute(async (req, res) => { const policy = normalizeReaderBindingRequest(req.body, { maxTtlDays: config.writerBindingMaxTtlDays, }); await assertRegisteredProductIds(policy.allowedDataProductIds); const token = createReaderToken(); const bindingId = randomUUID(); const result = await pool.query( `insert into external_data_plane_reader_bindings ( id, token_hash, tenant_id, connection_id, source_connection_id, provider_id, allowed_data_product_ids, expires_at ) values ($1, $2, $3, $4, $4, $5, $6::jsonb, $7) returning id, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [ bindingId, hashReaderToken(token), policy.tenantId, policy.connectionId, policy.providerId, JSON.stringify(policy.allowedDataProductIds), policy.expiresAt, ], ); sendOneTimeCapability(res, 201, { ok: true, readerBinding: safeReaderBinding(result.rows[0]), token }); })); app.post("/internal/data-plane/v1/reader-bindings/:bindingId/rotate", requireProvisionerApi, asyncRoute(async (req, res) => { const bindingId = requireUuid(req.params.bindingId, "reader_binding_id_invalid"); const token = createReaderToken(); const result = await pool.query( `update external_data_plane_reader_bindings set token_hash = $2, rotated_at = now() where id = $1 and binding_key is null and active = true and expires_at > now() returning id, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingId, hashReaderToken(token)], ); if (!result.rowCount) throw httpError(404, "reader_binding_not_found_or_inactive"); sendOneTimeCapability(res, 200, { ok: true, readerBinding: safeReaderBinding(result.rows[0]), token }); })); app.post("/internal/data-plane/v1/reader-bindings/:bindingId/revoke", requireProvisionerApi, asyncRoute(async (req, res) => { const bindingId = requireUuid(req.params.bindingId, "reader_binding_id_invalid"); const result = await pool.query( `update external_data_plane_reader_bindings set active = false, revoked_at = now() where id = $1 and binding_key is null and active = true returning id, tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt"`, [bindingId], ); if (!result.rowCount) throw httpError(404, "reader_binding_not_found_or_inactive"); res.json({ ok: true, readerBinding: safeReaderBinding(result.rows[0]) }); })); app.get("/internal/data-plane/v1/data-products/:dataProductId/snapshot", requireReaderBinding, asyncRoute(async (req, res) => { if (hasScopeHeaders(req)) throw httpError(400, "data_product_read_scope_headers_forbidden"); const dataProductId = assertReaderProduct(req.readerBinding, req.params.dataProductId); const definition = await loadDataProductDefinition(pool, dataProductId); if (!definition) throw httpError(404, "data_product_not_found"); const limit = boundedLimit(req.query.limit, 5000, 1, 5000); const snapshot = await readDataProductSnapshot(pool, req.readerBinding, definition, { limit }); res.json(snapshot); })); app.get("/internal/data-plane/v1/data-products/:dataProductId/history", requireReaderBinding, asyncRoute(async (req, res) => { if (hasScopeHeaders(req)) throw httpError(400, "data_product_read_scope_headers_forbidden"); const dataProductId = assertReaderProduct(req.readerBinding, req.params.dataProductId); const definition = await loadDataProductDefinition(pool, dataProductId); if (!definition) throw httpError(404, "data_product_not_found"); const history = await readDataProductHistory(pool, req.readerBinding, definition, { from: req.query.from, to: req.query.to, resolutionMs: req.query.resolutionMs, sourceIds: parseSourceIds(req.query.sourceIds), limit: req.query.limit, cursor: req.query.cursor, }); res.json(history); })); app.get("/internal/data-plane/v1/data-products/:dataProductId/stream", requireReaderBinding, asyncRoute(async (req, res) => { if (shuttingDown) throw httpError(503, "service_shutting_down"); if (hasScopeHeaders(req)) throw httpError(400, "data_product_read_scope_headers_forbidden"); const dataProductId = assertReaderProduct(req.readerBinding, req.params.dataProductId); const definition = await loadDataProductDefinition(pool, dataProductId); if (!definition) throw httpError(404, "data_product_not_found"); if (definition.deliveryMode !== "snapshot+patch") throw httpError(409, "data_product_stream_not_supported"); const streamCount = activeReaderStreams.get(req.readerBinding.id) || 0; if (streamCount >= config.maxReaderStreams) throw httpError(429, "reader_stream_limit_exceeded"); activeReaderStreams.set(req.readerBinding.id, streamCount + 1); activeStreamResponses.add(res); let poll = null; let heartbeat = null; let polling = false; let heartbeatWriting = false; let cleanedUp = false; const cleanup = () => { if (cleanedUp) return; cleanedUp = true; if (poll) clearInterval(poll); if (heartbeat) clearInterval(heartbeat); activeStreamResponses.delete(res); const remaining = Math.max(0, (activeReaderStreams.get(req.readerBinding.id) || 1) - 1); if (remaining) activeReaderStreams.set(req.readerBinding.id, remaining); else activeReaderStreams.delete(req.readerBinding.id); }; req.once("close", cleanup); res.once("close", cleanup); try { let cursor = parseCursor(req.get("last-event-id") || req.query.after || "0"); const initialEvents = await readPatchEvents(pool, req.readerBinding, definition, cursor); if (res.destroyed || cleanedUp) return; res.status(200); res.set({ "Cache-Control": "no-cache, no-transform", "Content-Type": "text/event-stream", Connection: "keep-alive", "X-Accel-Buffering": "no", }); res.flushHeaders(); await writeSseFrame(res, ": nodedc-data-product-stream\n\n"); await writeSseFrame(res, `event: nodedc.data-product.ready.v1\ndata: ${JSON.stringify({ schemaVersion: "nodedc.data-product.ready/v1", dataProductId, cursor: cursor.toString(), emittedAt: new Date().toISOString(), })}\n\n`); for (const event of initialEvents) { await writePatchEvent(res, event); cursor = BigInt(event.cursor); } const pump = async () => { if (polling || res.writableEnded || res.destroyed || shuttingDown) return; polling = true; try { await assertReaderStreamAccess(req.readerBinding, dataProductId); const events = await readPatchEvents(pool, req.readerBinding, definition, cursor); for (const event of events) { await writePatchEvent(res, event); cursor = BigInt(event.cursor); } } catch (error) { const code = safeErrorCode(error); if (!res.writableEnded && !res.destroyed) { await writeSseFrame(res, `event: error\ndata: ${JSON.stringify({ ok: false, error: code })}\n\n`).catch(() => {}); res.end(); } } finally { polling = false; } }; poll = setInterval(() => { void pump(); }, config.streamPollMs); poll.unref(); heartbeat = setInterval(() => { if (polling || heartbeatWriting || res.writableEnded || res.destroyed) return; heartbeatWriting = true; void writeSseFrame(res, `: heartbeat ${Date.now()}\n\n`) .catch(() => { if (!res.writableEnded) res.end(); }) .finally(() => { heartbeatWriting = false; }); }, config.streamHeartbeatMs); heartbeat.unref(); } catch (error) { cleanup(); if (res.headersSent) { if (!res.writableEnded) res.end(); return; } throw error; } })); app.get("/internal/data-plane/v1/data-products/:dataProductId/current", requireLegacyIntake, requireInternalApi, asyncRoute(async (req, res) => { const scope = requireScope(req); const dataProductId = String(req.params.dataProductId || ""); if (!isIdentifier(dataProductId)) throw httpError(400, "data_product_id_invalid"); const limit = boundedLimit(req.query.limit, 200, 1, 1000); const rows = await pool.query( `select provider_id as "providerId", data_product_id as "dataProductId", source_id as "sourceId", semantic_type as "semanticType", observed_at as "observedAt", received_at as "receivedAt", attributes, case when geometry is null then null else jsonb_build_object('type', 'Point', 'coordinates', jsonb_build_array( ST_X(geometry::geometry), ST_Y(geometry::geometry) )) end as geometry from external_data_plane_current where tenant_id = $1 and connection_id = $2 and data_product_id = $3 order by observed_at desc, source_id asc limit $4`, [scope.tenantId, scope.connectionId, dataProductId, limit], ); res.json({ ok: true, dataProductId, tenantId: scope.tenantId, connectionId: scope.connectionId, facts: rows.rows, }); })); app.get("/internal/data-plane/v1/status", requireLegacyIntake, requireInternalApi, asyncRoute(async (req, res) => { const scope = requireScope(req); const result = await pool.query( `select count(*)::integer as "currentFactCount", max(received_at) as "lastReceivedAt" from external_data_plane_current where tenant_id = $1 and connection_id = $2`, [scope.tenantId, scope.connectionId], ); res.json({ ok: true, ...scope, ...result.rows[0], providerLogic: "absent", commandTransport: "absent" }); })); app.use((error, _req, res, _next) => { const status = Number(error?.status || 500); const publicStatus = status >= 400 && status < 600 ? status : 500; console.error(JSON.stringify({ event: "external_data_plane_error", error: safeErrorCode(error), status: publicStatus })); res.status(publicStatus).json({ ok: false, error: publicStatus >= 500 ? "internal_error" : safeErrorCode(error) }); }); await migrate(pool); const bundledDataProducts = await reconcileDataProductDefinitions(pool); retentionSweepTimer = setInterval(() => { void sweepRetention().catch((error) => { console.error(JSON.stringify({ event: "external_data_plane_retention_sweep_failed", error: safeErrorCode(error) })); }); }, config.retentionSweepMs); retentionSweepTimer.unref(); httpServer.listen(config.port, "0.0.0.0", () => { console.log(`NODE.DC External Data Plane listening on http://0.0.0.0:${config.port}`); console.log(JSON.stringify({ event: "external_data_plane_definitions_ready", count: bundledDataProducts.length })); setImmediate(() => { void sweepRetention().catch((error) => { console.error(JSON.stringify({ event: "external_data_plane_retention_sweep_failed", error: safeErrorCode(error) })); }); }); }); process.on("SIGTERM", shutdown); process.on("SIGINT", shutdown); async function persistBatch(batch) { const client = await pool.connect(); try { await client.query("begin"); const existing = await client.query( `select id, fact_count as "factCount", inserted_fact_count as "insertedFactCount" from external_data_plane_batches where tenant_id = $1 and connection_id = $2 and provider_id = $3 and data_product_id = $4 and idempotency_key = $5 for update`, [ batch.source.tenantId, batch.source.connectionId, batch.source.providerId, batch.contract.dataProductId, batch.batch.idempotencyKey, ], ); if (existing.rowCount) { await client.query("commit"); return { batchId: existing.rows[0].id, idempotent: true, ...existing.rows[0] }; } const batchId = randomUUID(); await client.query( `insert into external_data_plane_batches ( id, tenant_id, connection_id, provider_id, data_product_id, contract_version, ontology_revision, run_id, sequence, idempotency_key, received_at, fact_count ) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [ batchId, batch.source.tenantId, batch.source.connectionId, batch.source.providerId, batch.contract.dataProductId, batch.contract.version, batch.contract.ontologyRevision, batch.batch.runId, batch.batch.sequence, batch.batch.idempotencyKey, batch.batch.receivedAt, batch.facts.length, ], ); if (batch.raw) await persistRawEnvelope(client, batchId, batch); const records = batch.facts.map((fact) => ({ id: randomUUID(), source_id: fact.sourceId, semantic_type: fact.semanticType, observed_at: fact.observedAt, attributes: fact.attributes || {}, geometry: fact.geometry || null, fingerprint: hash(fact), })); const inserted = await client.query( `insert into external_data_plane_facts ( id, batch_id, tenant_id, connection_id, provider_id, data_product_id, source_id, semantic_type, observed_at, received_at, attributes, geometry, fingerprint ) select item.id::uuid, $1, $2, $3, $4, $5, item.source_id, item.semantic_type, item.observed_at, $6, coalesce(item.attributes, '{}'::jsonb), case when item.geometry is null then null else ST_SetSRID(ST_MakePoint( (item.geometry->'coordinates'->>0)::double precision, (item.geometry->'coordinates'->>1)::double precision ), 4326)::geography end, item.fingerprint from jsonb_to_recordset($7::jsonb) as item( id text, source_id text, semantic_type text, observed_at timestamptz, attributes jsonb, geometry jsonb, fingerprint text ) on conflict do nothing returning id`, [ batchId, batch.source.tenantId, batch.source.connectionId, batch.source.providerId, batch.contract.dataProductId, batch.batch.receivedAt, JSON.stringify(records), ], ); await client.query( `insert into external_data_plane_current ( tenant_id, connection_id, provider_id, data_product_id, source_id, semantic_type, observed_at, received_at, attributes, geometry, fingerprint, updated_at ) select tenant_id, connection_id, provider_id, data_product_id, source_id, semantic_type, observed_at, received_at, attributes, geometry, fingerprint, now() from external_data_plane_facts where batch_id = $1 on conflict (tenant_id, connection_id, provider_id, data_product_id, source_id, semantic_type) do update set observed_at = excluded.observed_at, received_at = excluded.received_at, attributes = excluded.attributes, geometry = excluded.geometry, fingerprint = excluded.fingerprint, updated_at = now() where excluded.observed_at >= external_data_plane_current.observed_at`, [batchId], ); await client.query( "update external_data_plane_batches set inserted_fact_count = $2 where id = $1", [batchId, inserted.rowCount], ); await client.query("commit"); return { batchId, idempotent: false, factCount: batch.facts.length, insertedFactCount: inserted.rowCount }; } catch (error) { await client.query("rollback"); throw error; } finally { client.release(); } } async function persistRawEnvelope(client, batchId, batch) { const serialized = batch.raw.payload === undefined ? null : JSON.stringify(batch.raw.payload); const expiresAt = rawRetentionExpiry({ rawRetentionDays: config.rawRetentionDays }); await client.query( `insert into external_data_plane_raw_envelopes ( id, batch_id, payload_hash, content_type, payload, payload_ref, payload_bytes, received_at, expires_at ) values ($1, $2, $3, $4, $5::jsonb, $6, $7, $8, $9)`, [ randomUUID(), batchId, batch.raw.hash || hash(batch.raw.payload), batch.raw.contentType, serialized, batch.raw.ref || null, serialized ? Buffer.byteLength(serialized) : null, batch.batch.receivedAt, expiresAt, ], ); } function requireInternalApi(req, _res, next) { if (!config.internalAccessToken) return next(httpError(503, "internal_api_not_configured")); const value = bearerToken(req); if (!value || !safeEqual(value, config.internalAccessToken)) return next(httpError(401, "unauthorized")); return next(); } function requireLegacyIntake(_req, _res, next) { if (!config.legacyIntakeEnabled) return next(httpError(410, "legacy_intake_disabled")); return next(); } function requireProvisionerApi(req, _res, next) { if (!config.provisionerApiEnabled) return next(httpError(503, "provisioner_api_disabled")); return requireProvisionerCredential(req, next); } function requireManagedProvisionerApi(req, _res, next) { if (!config.managedProvisionerApiEnabled) return next(httpError(503, "managed_provisioner_api_disabled")); if (!verifyManagedProvisionerRequest) return next(httpError(503, "managed_provisioner_api_not_configured")); try { req.managedProvisionerIdentity = verifyManagedProvisionerRequest(req); return next(); } catch (error) { return next(error); } } function requireFoundryProvisionerApi(req, _res, next) { if (!config.foundryProvisionerApiEnabled) return next(httpError(503, "foundry_provisioner_api_disabled")); if (!verifyFoundryProvisionerRequest) return next(httpError(503, "foundry_provisioner_api_not_configured")); try { req.managedProvisionerIdentity = verifyFoundryProvisionerRequest(req); return next(); } catch (error) { return next(error); } } function requireProvisionerCredential(req, next) { if (!config.provisionerAccessToken) return next(httpError(503, "provisioner_api_not_configured")); const value = bearerToken(req); if (!value || !safeEqual(value, config.provisionerAccessToken)) return next(httpError(401, "provisioner_unauthorized")); return next(); } function requireWriterBinding(req, _res, next) { return resolveWriterBinding(req) .then((binding) => { req.writerBinding = binding; next(); }) .catch(next); } function requireReaderBinding(req, _res, next) { return resolveReaderBinding(req) .then((binding) => { req.readerBinding = binding; next(); }) .catch(next); } async function resolveWriterBinding(req) { const token = bearerToken(req); if (!token) throw httpError(401, "writer_binding_unauthorized"); const result = await pool.query( `select id, tenant_id as "tenantId", connection_id as "connectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_writer_bindings where token_hash = $1 and active = true and (expires_at is null or expires_at > now())`, [hashWriterToken(token)], ); if (!result.rowCount) throw httpError(401, "writer_binding_unauthorized"); return result.rows[0]; } async function resolveReaderBinding(req) { const token = bearerToken(req); if (!token) throw httpError(401, "reader_binding_unauthorized"); const result = await pool.query( `select id, binding_key as "bindingKey", token_hash as "tokenHash", tenant_id as "tenantId", connection_id as "connectionId", source_connection_id as "sourceConnectionId", provider_id as "providerId", allowed_data_product_ids as "allowedDataProductIds", expires_at as "expiresAt", active, created_at as "createdAt", rotated_at as "rotatedAt", revoked_at as "revokedAt" from external_data_plane_reader_bindings where token_hash = $1 and active = true and (expires_at is null or expires_at > now())`, [hashReaderToken(token)], ); if (!result.rowCount) throw httpError(401, "reader_binding_unauthorized"); if (result.rows[0].bindingKey && !result.rows[0].sourceConnectionId) { throw httpError(409, "reader_source_scope_unresolved"); } return result.rows[0]; } async function assertReaderStreamAccess(binding, dataProductId) { const result = await pool.query( `select binding.id from external_data_plane_reader_bindings as binding join external_data_plane_products as product on product.id = $3 and product.active = true where binding.id = $1 and binding.token_hash = $2 and binding.active = true and (binding.expires_at is null or binding.expires_at > now()) and binding.allowed_data_product_ids ? $3`, [binding.id, binding.tokenHash, dataProductId], ); if (!result.rowCount) throw httpError(401, "reader_stream_access_revoked"); } async function assertLegacyBatchProduct(batch) { const definition = await loadDataProductDefinition(pool, batch.contract.dataProductId); if (!definition) throw httpError(422, "legacy_data_product_not_registered"); if (definition.version !== batch.contract.version || definition.ontologyRevision !== batch.contract.ontologyRevision) { throw httpError(422, "legacy_data_product_contract_mismatch"); } assertPublishMatchesDefinition(batch, definition); } async function listGrantedProducts(binding) { const allowed = Array.isArray(binding.allowedDataProductIds) ? binding.allowedDataProductIds : []; if (!allowed.length) return []; const result = await pool.query( `select id, version, ontology_revision as "ontologyRevision", delivery_mode as "deliveryMode", semantic_types as "semanticTypes", fields, field_contracts as "fieldContracts", history_policy as "historyPolicy", active, created_at as "createdAt", updated_at as "updatedAt" from external_data_plane_products where active = true and id = any($1::text[]) order by id asc`, [allowed], ); return result.rows.map(safeDataProductDefinition); } async function assertRegisteredProductIds(dataProductIds) { const result = await pool.query( "select id from external_data_plane_products where active = true and id = any($1::text[])", [dataProductIds], ); const registered = new Set(result.rows.map((row) => row.id)); if (dataProductIds.some((id) => !registered.has(id))) throw httpError(422, "binding_data_product_not_registered"); } function requireScope(req) { const tenantId = String(req.get("x-nodedc-tenant-id") || ""); const connectionId = String(req.get("x-nodedc-connection-id") || ""); if (!isIdentifier(tenantId) || !isIdentifier(connectionId)) throw httpError(400, "scope_headers_required"); return { tenantId, connectionId }; } function hasScopeHeaders(req) { return Object.hasOwn(req.headers, "x-nodedc-tenant-id") || Object.hasOwn(req.headers, "x-nodedc-connection-id"); } function bearerToken(req) { return String(req.get("authorization") || "").replace(/^Bearer\s+/i, ""); } function requireUuid(value, code) { const normalized = String(value || ""); if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(normalized)) { throw httpError(400, code); } return normalized; } function requireIdentifier(value, code) { const normalized = String(value || ""); if (!isIdentifier(normalized)) throw httpError(400, code); return normalized; } function requirePositiveInteger(value, code) { const normalized = String(value ?? ""); if (!/^[1-9]\d*$/.test(normalized)) throw httpError(400, code); const number = Number(normalized); if (!Number.isSafeInteger(number) || number > 2_147_483_647) throw httpError(400, code); return number; } function parseCursor(value) { const normalized = String(value ?? ""); if (!/^(?:0|[1-9]\d*)$/.test(normalized)) throw httpError(400, "data_product_cursor_invalid"); const cursor = BigInt(normalized); if (cursor > 9_223_372_036_854_775_807n) throw httpError(400, "data_product_cursor_invalid"); return cursor; } function parseSourceIds(value) { if (value === undefined || value === null || value === "") return []; const values = Array.isArray(value) ? value : String(value).split(","); return values.map((item) => String(item || "").trim()).filter(Boolean); } function writePatchEvent(res, event) { return writeSseFrame(res, `id: ${event.cursor}\nevent: nodedc.data-product.patch.v1\ndata: ${JSON.stringify(event)}\n\n`); } function writeSseFrame(res, frame) { if (res.writableEnded || res.destroyed) return Promise.reject(httpError(499, "reader_stream_closed")); if (res.write(frame)) return Promise.resolve(); return new Promise((resolve, reject) => { const cleanup = () => { res.off("drain", onDrain); res.off("close", onClose); res.off("error", onError); }; const onDrain = () => { cleanup(); resolve(); }; const onClose = () => { cleanup(); reject(httpError(499, "reader_stream_closed")); }; const onError = (error) => { cleanup(); reject(error); }; res.once("drain", onDrain); res.once("close", onClose); res.once("error", onError); }); } function sendOneTimeCapability(res, status, payload) { // Create/rotate is the only boundary where a capability exists in // plaintext. A trusted provisioner must consume the response in memory and // place it directly into its opaque destination; intermediaries must never // cache or persist it. res.set({ "Cache-Control": "no-store, max-age=0", Pragma: "no-cache", Expires: "0", }); return res.status(status).json(payload); } function safeEqual(left, right) { const leftBuffer = Buffer.from(left); const rightBuffer = Buffer.from(right); return leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer); } function boundedLimit(value, fallback, min, max) { const candidate = Number.parseInt(String(value ?? ""), 10); if (!Number.isInteger(candidate)) return fallback; return Math.max(min, Math.min(max, candidate)); } function hash(value) { return createHash("sha256").update(JSON.stringify(value)).digest("hex"); } function isIdentifier(value) { return /^[a-z][a-z0-9._:-]{2,127}$/i.test(value); } function asyncRoute(handler) { return (req, res, next) => Promise.resolve(handler(req, res, next)).catch(next); } function httpError(status, code) { return Object.assign(new Error(code), { status, code }); } function safeErrorCode(error) { return String(error?.code || error?.message || "internal_error").replace(/[^a-z0-9_.:-]/gi, "_").slice(0, 120); } async function sweepExpiredRawEnvelopes() { const result = await pool.query( `with expired as ( select id from external_data_plane_raw_envelopes where expires_at < now() order by expires_at asc limit $1 ) delete from external_data_plane_raw_envelopes as target using expired where target.id = expired.id`, [config.retentionDeleteLimit], ); return result.rowCount; } async function sweepRetention() { const rawEnvelopeCount = await sweepExpiredRawEnvelopes(); const patchEventCount = await prunePatchOutbox(pool, { retentionMs: config.patchRetentionMs, limit: config.retentionDeleteLimit, }); const historyFactCount = await pruneDataProductHistory(pool, { limit: config.retentionDeleteLimit }); const batchReceiptCount = await pruneBatchReceipts(pool, { retentionMs: config.receiptRetentionMs, limit: config.retentionDeleteLimit, }); lastRetentionSweepAt = new Date().toISOString(); return { rawEnvelopeCount, patchEventCount, historyFactCount, batchReceiptCount }; } async function shutdown() { if (shutdownPromise) return shutdownPromise; shutdownPromise = (async () => { shuttingDown = true; if (retentionSweepTimer) clearInterval(retentionSweepTimer); for (const response of activeStreamResponses) { if (!response.writableEnded) response.end(); } const closed = new Promise((resolve) => httpServer.close(resolve)); httpServer.closeIdleConnections?.(); const forceClose = setTimeout(() => httpServer.closeAllConnections?.(), 250); await Promise.race([closed, new Promise((resolve) => setTimeout(resolve, 2_000))]); clearTimeout(forceClose); httpServer.closeAllConnections?.(); await pool.end(); })(); return shutdownPromise; }