@harperfast/harper 5.2.13 → 5.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/mcpTools.ts +1 -1
- package/agent/session.ts +25 -14
- package/bin/cliOperations.ts +58 -9
- package/bin/copyDb.ts +282 -59
- package/bin/deploySetup.ts +16 -5
- package/bin/harper.ts +1 -1
- package/bin/help.ts +4 -1
- package/bin/lite.ts +4 -1
- package/bin/restart.ts +120 -4
- package/bin/run.ts +6 -11
- package/bin/upgrade.js +7 -3
- package/bin/workloadIdentity.ts +119 -0
- package/components/Application.ts +3436 -242
- package/components/ApplicationScope.ts +8 -0
- package/components/EntryHandler.ts +59 -39
- package/components/OptionsWatcher.ts +440 -98
- package/components/RuntimeModuleTracker.ts +38 -7
- package/components/Scope.ts +56 -15
- package/components/awaitRestart.ts +84 -0
- package/components/componentLoader.ts +381 -32
- package/components/componentPreparationLock.ts +16 -5
- package/components/deploymentOperations.ts +4 -1
- package/components/deploymentRecorder.ts +9 -2
- package/components/mcp/adapters/harperHttp.ts +4 -0
- package/components/mcp/listChanged.ts +4 -0
- package/components/mcp/toolRegistry.ts +2 -0
- package/components/mcp/tools/operations.ts +9 -0
- package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
- package/components/operations.js +537 -113
- package/components/operationsValidation.js +98 -3
- package/components/packageComponent.ts +25 -1
- package/components/requestRestart.ts +11 -0
- package/components/status/ComponentStatusRegistry.ts +59 -0
- package/config/RootConfigWatcher.ts +240 -40
- package/config/configReadRetry.ts +62 -0
- package/config/configUtils.ts +357 -48
- package/config/harperConfigEnvVars.ts +170 -27
- package/config/parseConfigFile.ts +34 -0
- package/config/readConfigFileSync.ts +44 -0
- package/config/watcherArming.ts +59 -0
- package/config-root.schema.json +33 -0
- package/dataLayer/blobBackup.ts +160 -50
- package/dataLayer/delete.ts +6 -1
- package/dataLayer/harperBridge/ResourceBridge.ts +80 -10
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dataLayer/hdbInfoController.ts +34 -1
- package/dataLayer/insert.ts +44 -1
- package/dataLayer/rocksdbBackup.ts +53 -10
- package/dataLayer/schema.ts +11 -1
- package/dataLayer/schemaDescribe.ts +8 -1
- package/dist/agent/mcpTools.js +1 -1
- package/dist/agent/mcpTools.js.map +1 -1
- package/dist/agent/session.d.ts +22 -0
- package/dist/agent/session.js +26 -15
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cliOperations.js +61 -9
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/copyDb.d.ts +12 -1
- package/dist/bin/copyDb.js +248 -60
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/bin/deploySetup.d.ts +2 -0
- package/dist/bin/deploySetup.js +11 -3
- package/dist/bin/deploySetup.js.map +1 -1
- package/dist/bin/harper.js +1 -1
- package/dist/bin/harper.js.map +1 -1
- package/dist/bin/help.js +4 -1
- package/dist/bin/help.js.map +1 -1
- package/dist/bin/lite.js +4 -1
- package/dist/bin/lite.js.map +1 -1
- package/dist/bin/restart.js +91 -6
- package/dist/bin/restart.js.map +1 -1
- package/dist/bin/run.js +4 -10
- package/dist/bin/run.js.map +1 -1
- package/dist/bin/upgrade.js +4 -3
- package/dist/bin/upgrade.js.map +1 -1
- package/dist/bin/workloadIdentity.d.ts +18 -0
- package/dist/bin/workloadIdentity.js +100 -0
- package/dist/bin/workloadIdentity.js.map +1 -0
- package/dist/components/Application.d.ts +235 -17
- package/dist/components/Application.js +3038 -238
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +8 -0
- package/dist/components/ApplicationScope.js +7 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/EntryHandler.js +26 -10
- package/dist/components/EntryHandler.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +4 -0
- package/dist/components/OptionsWatcher.js +440 -99
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/RuntimeModuleTracker.js +40 -6
- package/dist/components/RuntimeModuleTracker.js.map +1 -1
- package/dist/components/Scope.js +52 -13
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/awaitRestart.d.ts +33 -0
- package/dist/components/awaitRestart.js +61 -0
- package/dist/components/awaitRestart.js.map +1 -0
- package/dist/components/componentLoader.d.ts +38 -1
- package/dist/components/componentLoader.js +313 -24
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentPreparationLock.d.ts +5 -0
- package/dist/components/componentPreparationLock.js +14 -6
- package/dist/components/componentPreparationLock.js.map +1 -1
- package/dist/components/deploymentOperations.js +4 -1
- package/dist/components/deploymentOperations.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +4 -2
- package/dist/components/deploymentRecorder.js +1 -0
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.js +4 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/listChanged.js +5 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +1 -0
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +5 -0
- package/dist/components/mcp/tools/operations.js +9 -0
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/operations.d.ts +28 -0
- package/dist/components/operations.js +476 -113
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +97 -3
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/packageComponent.js +24 -0
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/components/requestRestart.d.ts +1 -0
- package/dist/components/requestRestart.js +7 -0
- package/dist/components/requestRestart.js.map +1 -1
- package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
- package/dist/components/status/ComponentStatusRegistry.js +63 -0
- package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +9 -1
- package/dist/config/RootConfigWatcher.js +228 -37
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configReadRetry.d.ts +8 -0
- package/dist/config/configReadRetry.js +62 -0
- package/dist/config/configReadRetry.js.map +1 -0
- package/dist/config/configUtils.d.ts +11 -2
- package/dist/config/configUtils.js +304 -47
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/harperConfigEnvVars.d.ts +16 -0
- package/dist/config/harperConfigEnvVars.js +162 -25
- package/dist/config/harperConfigEnvVars.js.map +1 -1
- package/dist/config/parseConfigFile.d.ts +4 -0
- package/dist/config/parseConfigFile.js +35 -0
- package/dist/config/parseConfigFile.js.map +1 -0
- package/dist/config/readConfigFileSync.d.ts +1 -0
- package/dist/config/readConfigFileSync.js +47 -0
- package/dist/config/readConfigFileSync.js.map +1 -0
- package/dist/config/watcherArming.d.ts +15 -0
- package/dist/config/watcherArming.js +59 -0
- package/dist/config/watcherArming.js.map +1 -0
- package/dist/dataLayer/blobBackup.d.ts +49 -20
- package/dist/dataLayer/blobBackup.js +139 -50
- package/dist/dataLayer/blobBackup.js.map +1 -1
- package/dist/dataLayer/delete.js +1 -1
- package/dist/dataLayer/delete.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +72 -12
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
- package/dist/dataLayer/hdbInfoController.d.ts +10 -0
- package/dist/dataLayer/hdbInfoController.js +30 -1
- package/dist/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/dataLayer/insert.d.ts +9 -1
- package/dist/dataLayer/insert.js +30 -0
- package/dist/dataLayer/insert.js.map +1 -1
- package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
- package/dist/dataLayer/rocksdbBackup.js +45 -8
- package/dist/dataLayer/rocksdbBackup.js.map +1 -1
- package/dist/dataLayer/schema.js +8 -0
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +8 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +55 -0
- package/dist/resources/DatabaseTransaction.d.ts +77 -0
- package/dist/resources/DatabaseTransaction.js +586 -54
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -1
- package/dist/resources/LMDBTransaction.js +43 -6
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
- package/dist/resources/PrimaryRocksDatabase.js +49 -5
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +20 -0
- package/dist/resources/RecordEncoder.js +110 -9
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +2 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.js +117 -24
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +28 -1
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/RocksIndexStore.d.ts +6 -1
- package/dist/resources/RocksIndexStore.js +24 -9
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +24 -1
- package/dist/resources/RocksTransactionLogStore.js +160 -49
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +165 -10
- package/dist/resources/Table.js +1805 -303
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.js +10 -3
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +181 -2
- package/dist/resources/auditStore.js +640 -29
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/blob.d.ts +129 -9
- package/dist/resources/blob.js +992 -114
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/branchDatabase.d.ts +48 -0
- package/dist/resources/branchDatabase.js +892 -0
- package/dist/resources/branchDatabase.js.map +1 -0
- package/dist/resources/crdt.js +50 -12
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/dataLoader.js +3 -4
- package/dist/resources/dataLoader.js.map +1 -1
- package/dist/resources/databases.d.ts +157 -14
- package/dist/resources/databases.js +1462 -325
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/defineTable.d.ts +10 -2
- package/dist/resources/defineTable.js +9 -1
- package/dist/resources/defineTable.js.map +1 -1
- package/dist/resources/derivedIndexRegistry.d.ts +5 -0
- package/dist/resources/derivedIndexRegistry.js +68 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -0
- package/dist/resources/derivedIndexRuntime.d.ts +215 -0
- package/dist/resources/derivedIndexRuntime.js +2027 -0
- package/dist/resources/derivedIndexRuntime.js.map +1 -0
- package/dist/resources/graphql.d.ts +1 -1
- package/dist/resources/graphql.js +52 -16
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +103 -9
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +854 -41
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
- package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
- package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
- package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
- package/dist/resources/longLivedTransactions.d.ts +71 -0
- package/dist/resources/longLivedTransactions.js +358 -0
- package/dist/resources/longLivedTransactions.js.map +1 -0
- package/dist/resources/models/backendRegistry.d.ts +26 -0
- package/dist/resources/models/backendRegistry.js +60 -2
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/bootstrap.d.ts +33 -1
- package/dist/resources/models/bootstrap.js +416 -31
- package/dist/resources/models/bootstrap.js.map +1 -1
- package/dist/resources/nodeIdMapping.d.ts +5 -0
- package/dist/resources/nodeIdMapping.js +49 -0
- package/dist/resources/nodeIdMapping.js.map +1 -1
- package/dist/resources/recordLock.d.ts +123 -0
- package/dist/resources/recordLock.js +315 -0
- package/dist/resources/recordLock.js.map +1 -0
- package/dist/resources/recordLockCoordinator.d.ts +557 -0
- package/dist/resources/recordLockCoordinator.js +2565 -0
- package/dist/resources/recordLockCoordinator.js.map +1 -0
- package/dist/resources/replayLogs.d.ts +14 -1
- package/dist/resources/replayLogs.js +157 -24
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replayLogsGuards.d.ts +94 -7
- package/dist/resources/replayLogsGuards.js +111 -7
- package/dist/resources/replayLogsGuards.js.map +1 -1
- package/dist/resources/replicatedApplyFailure.d.ts +16 -0
- package/dist/resources/replicatedApplyFailure.js +63 -0
- package/dist/resources/replicatedApplyFailure.js.map +1 -0
- package/dist/resources/scheduler/scheduler.js +3 -3
- package/dist/resources/scheduler/scheduler.js.map +1 -1
- package/dist/resources/search.d.ts +10 -4
- package/dist/resources/search.js +283 -37
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/tracked.d.ts +5 -1
- package/dist/resources/tracked.js +74 -23
- package/dist/resources/tracked.js.map +1 -1
- package/dist/resources/transactionBroadcast.d.ts +1 -1
- package/dist/resources/transactionBroadcast.js +2 -2
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/security/auth.js +61 -30
- package/dist/security/auth.js.map +1 -1
- package/dist/security/authn/oidc/claims.d.ts +22 -0
- package/dist/security/authn/oidc/claims.js +71 -0
- package/dist/security/authn/oidc/claims.js.map +1 -0
- package/dist/security/authn/oidc/identityToken.d.ts +27 -0
- package/dist/security/authn/oidc/identityToken.js +111 -0
- package/dist/security/authn/oidc/identityToken.js.map +1 -0
- package/dist/security/authn/oidc/jwks.d.ts +25 -0
- package/dist/security/authn/oidc/jwks.js +261 -0
- package/dist/security/authn/oidc/jwks.js.map +1 -0
- package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
- package/dist/security/authn/oidc/providers/generic.js +34 -0
- package/dist/security/authn/oidc/providers/generic.js.map +1 -0
- package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
- package/dist/security/authn/oidc/providers/githubActions.js +129 -0
- package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
- package/dist/security/authn/oidc/providers/index.d.ts +37 -0
- package/dist/security/authn/oidc/providers/index.js +24 -0
- package/dist/security/authn/oidc/providers/index.js.map +1 -0
- package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
- package/dist/security/authn/oidc/tokenExchange.js +306 -0
- package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
- package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
- package/dist/security/authn/oidc/types.d.ts +38 -0
- package/dist/security/authn/oidc/types.js +6 -0
- package/dist/security/authn/oidc/types.js.map +1 -0
- package/dist/security/certificateVerification/index.js +40 -11
- package/dist/security/certificateVerification/index.js.map +1 -1
- package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
- package/dist/security/certificateVerification/trustedIssuers.js +79 -0
- package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
- package/dist/security/certificateVerification/types.d.ts +1 -0
- package/dist/security/credentialProvenance.d.ts +35 -0
- package/dist/security/credentialProvenance.js +51 -0
- package/dist/security/credentialProvenance.js.map +1 -0
- package/dist/security/credentialRejection.d.ts +4 -0
- package/dist/security/credentialRejection.js +24 -0
- package/dist/security/credentialRejection.js.map +1 -0
- package/dist/security/deferredAuthentication.d.ts +36 -0
- package/dist/security/deferredAuthentication.js +70 -0
- package/dist/security/deferredAuthentication.js.map +1 -0
- package/dist/security/impersonation.d.ts +21 -0
- package/dist/security/impersonation.js +108 -9
- package/dist/security/impersonation.js.map +1 -1
- package/dist/security/jsLoader.d.ts +6 -0
- package/dist/security/jsLoader.js +77 -15
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/keys.js +301 -71
- package/dist/security/keys.js.map +1 -1
- package/dist/security/operationScope.d.ts +21 -0
- package/dist/security/operationScope.js +36 -0
- package/dist/security/operationScope.js.map +1 -0
- package/dist/security/permissionsTranslator.js +21 -0
- package/dist/security/permissionsTranslator.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +19 -1
- package/dist/security/tokenAuthentication.js +191 -10
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/security/user.js +4 -3
- package/dist/security/user.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
- package/dist/server/DurableSubscriptionsSession.js +67 -10
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +106 -2
- package/dist/server/REST.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -0
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +16 -1
- package/dist/server/http.js +122 -17
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +8 -1
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/jobs/jobProcess.js +6 -2
- package/dist/server/jobs/jobProcess.js.map +1 -1
- package/dist/server/jobs/jobs.js +4 -1
- package/dist/server/jobs/jobs.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +26 -4
- package/dist/server/liveSubscriptionAuth.js +105 -39
- package/dist/server/liveSubscriptionAuth.js.map +1 -1
- package/dist/server/loadRootComponents.js +49 -10
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/mqtt.d.ts +2 -0
- package/dist/server/mqtt.js +165 -30
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/nodeName.d.ts +2 -0
- package/dist/server/nodeName.js +107 -23
- package/dist/server/nodeName.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +27 -0
- package/dist/server/serverHelpers/Headers.js +145 -1
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
- package/dist/server/serverHelpers/Request.d.ts +5 -10
- package/dist/server/serverHelpers/Request.js +38 -136
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +11 -0
- package/dist/server/serverHelpers/contentTypes.js +221 -40
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
- package/dist/server/serverHelpers/deployValidationState.js +9 -19
- package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
- package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
- package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
- package/dist/server/serverHelpers/registeredOperations.js +74 -21
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
- package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
- package/dist/server/serverHelpers/requestSanitization.js +20 -0
- package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +6 -3
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
- package/dist/server/serverHelpers/serverUtilities.js +178 -29
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
- package/dist/server/serverHelpers/uwsServer.js +19 -1
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/static.js +24 -28
- package/dist/server/static.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +5 -0
- package/dist/server/storageReclamation.js +17 -1
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/isolatedApplications.d.ts +47 -0
- package/dist/server/threads/isolatedApplications.js +171 -0
- package/dist/server/threads/isolatedApplications.js.map +1 -0
- package/dist/server/threads/itc.d.ts +7 -2
- package/dist/server/threads/itc.js +5 -1
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/logRotationTransport.d.ts +1 -0
- package/dist/server/threads/logRotationTransport.js +33 -0
- package/dist/server/threads/logRotationTransport.js.map +1 -0
- package/dist/server/threads/manageThreads.d.ts +83 -3
- package/dist/server/threads/manageThreads.js +769 -75
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -0
- package/dist/server/threads/socketRouter.js +277 -31
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadHeapMemory.d.ts +2 -0
- package/dist/server/threads/threadHeapMemory.js +31 -0
- package/dist/server/threads/threadHeapMemory.js.map +1 -0
- package/dist/server/threads/threadServer.js +76 -22
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/config.d.ts +1 -3
- package/dist/sqlEngine/config.js +19 -16
- package/dist/sqlEngine/config.js.map +1 -1
- package/dist/sqlTranslator/index.d.ts +1 -1
- package/dist/sqlTranslator/index.js +30 -7
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/upgrade/directives/5-3-0.d.ts +7 -0
- package/dist/upgrade/directives/5-3-0.js +148 -0
- package/dist/upgrade/directives/5-3-0.js.map +1 -0
- package/dist/upgrade/directives/directivesController.js +2 -1
- package/dist/upgrade/directives/directivesController.js.map +1 -1
- package/dist/utility/OperationFunctionCaller.js +2 -1
- package/dist/utility/OperationFunctionCaller.js.map +1 -1
- package/dist/utility/common_utils.d.ts +16 -0
- package/dist/utility/common_utils.js +32 -6
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/componentNames.d.ts +8 -0
- package/dist/utility/componentNames.js +12 -1
- package/dist/utility/componentNames.js.map +1 -1
- package/dist/utility/environment/environmentManager.js +3 -6
- package/dist/utility/environment/environmentManager.js.map +1 -1
- package/dist/utility/environment/systemInformation.d.ts +1 -0
- package/dist/utility/environment/systemInformation.js +1 -0
- package/dist/utility/environment/systemInformation.js.map +1 -1
- package/dist/utility/errors/commonErrors.d.ts +2 -0
- package/dist/utility/errors/commonErrors.js +2 -0
- package/dist/utility/errors/commonErrors.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +33 -0
- package/dist/utility/errors/hdbError.js +58 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +18 -0
- package/dist/utility/hdbTerms.js +20 -2
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +2 -0
- package/dist/utility/logging/harper_logger.js +286 -30
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
- package/dist/utility/logging/logGenerationCoordinator.js +184 -0
- package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
- package/dist/utility/logging/logRotation.d.ts +46 -0
- package/dist/utility/logging/logRotation.js +365 -0
- package/dist/utility/logging/logRotation.js.map +1 -0
- package/dist/utility/logging/logRotator.d.ts +1 -1
- package/dist/utility/logging/logRotator.js +192 -85
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/nodeIdentity.d.ts +9 -0
- package/dist/utility/nodeIdentity.js +58 -0
- package/dist/utility/nodeIdentity.js.map +1 -0
- package/dist/utility/npmUtilities.js +11 -7
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +3 -1
- package/dist/utility/operationPermissions.js +16 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +10 -7
- package/dist/utility/operation_authorization.js +212 -41
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/watchPath.d.ts +29 -0
- package/dist/utility/watchPath.js +68 -0
- package/dist/utility/watchPath.js.map +1 -0
- package/dist/utility/watcherFallback.d.ts +41 -0
- package/dist/utility/watcherFallback.js +153 -0
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.d.ts +12 -0
- package/dist/validation/configValidator.js +205 -75
- package/dist/validation/configValidator.js.map +1 -1
- package/dist/validation/installValidator.js +12 -0
- package/dist/validation/installValidator.js.map +1 -1
- package/dist/validation/validationWrapper.d.ts +11 -0
- package/dist/validation/validationWrapper.js +16 -3
- package/dist/validation/validationWrapper.js.map +1 -1
- package/index.ts +8 -0
- package/json/systemSchema.json +55 -0
- package/npm-shrinkwrap.json +286 -194
- package/package.json +15 -7
- package/resources/DESIGN.md +219 -52
- package/resources/DatabaseTransaction.ts +657 -52
- package/resources/LMDBTransaction.ts +46 -6
- package/resources/PrimaryRocksDatabase.ts +46 -7
- package/resources/RecordEncoder.ts +124 -9
- package/resources/RequestTarget.ts +2 -0
- package/resources/Resource.ts +114 -22
- package/resources/ResourceInterface.ts +31 -0
- package/resources/RocksIndexStore.ts +30 -9
- package/resources/RocksTransactionLogStore.ts +188 -51
- package/resources/Table.ts +1960 -301
- package/resources/analytics/write.ts +10 -3
- package/resources/auditStore.ts +647 -32
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/dataLoader.ts +3 -4
- package/resources/databases.ts +1584 -320
- package/resources/defineTable.ts +18 -2
- package/resources/derivedIndexRegistry.ts +56 -0
- package/resources/derivedIndexRuntime.ts +2292 -0
- package/resources/graphql.ts +72 -17
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +911 -48
- package/resources/indexes/hnswDerivedIndex.ts +531 -0
- package/resources/indexes/hnswPlaneBinding.ts +174 -0
- package/resources/longLivedTransactions.ts +360 -0
- package/resources/models/backendRegistry.ts +84 -2
- package/resources/models/bootstrap.ts +473 -28
- package/resources/nodeIdMapping.ts +50 -0
- package/resources/recordLock.ts +419 -0
- package/resources/recordLockCoordinator.ts +3043 -0
- package/resources/replayLogs.ts +152 -26
- package/resources/replayLogsGuards.ts +171 -8
- package/resources/replicatedApplyFailure.ts +77 -0
- package/resources/scheduler/scheduler.ts +4 -4
- package/resources/search.ts +288 -46
- package/resources/tracked.ts +73 -22
- package/resources/transactionBroadcast.ts +3 -3
- package/security/auth.ts +68 -29
- package/security/authn/oidc/claims.ts +72 -0
- package/security/authn/oidc/identityToken.ts +129 -0
- package/security/authn/oidc/jwks.ts +260 -0
- package/security/authn/oidc/providers/generic.ts +40 -0
- package/security/authn/oidc/providers/githubActions.ts +137 -0
- package/security/authn/oidc/providers/index.ts +52 -0
- package/security/authn/oidc/tokenExchange.ts +300 -0
- package/security/authn/oidc/trustPolicyOperations.ts +343 -0
- package/security/authn/oidc/types.ts +41 -0
- package/security/certificateVerification/index.ts +54 -13
- package/security/certificateVerification/trustedIssuers.ts +76 -0
- package/security/certificateVerification/types.ts +1 -0
- package/security/credentialProvenance.ts +47 -0
- package/security/credentialRejection.ts +22 -0
- package/security/deferredAuthentication.ts +71 -0
- package/security/impersonation.ts +117 -12
- package/security/jsLoader.ts +83 -18
- package/security/keys.ts +298 -72
- package/security/operationScope.ts +33 -0
- package/security/permissionsTranslator.js +23 -0
- package/security/tokenAuthentication.ts +233 -12
- package/security/user.ts +4 -3
- package/server/DESIGN.md +194 -16
- package/server/DurableSubscriptionsSession.ts +71 -11
- package/server/REST.ts +115 -4
- package/server/graphqlQuerying.ts +4 -0
- package/server/http.ts +133 -20
- package/server/itc/serverHandlers.js +8 -1
- package/server/jobs/jobProcess.ts +8 -2
- package/server/jobs/jobs.ts +4 -1
- package/server/liveSubscriptionAuth.ts +129 -46
- package/server/loadRootComponents.js +50 -8
- package/server/mqtt.ts +179 -38
- package/server/nodeName.ts +103 -21
- package/server/serverHelpers/Headers.ts +135 -0
- package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
- package/server/serverHelpers/Request.ts +33 -131
- package/server/serverHelpers/contentTypes.ts +217 -36
- package/server/serverHelpers/deployValidationState.ts +24 -13
- package/server/serverHelpers/operationAuthorizationState.ts +34 -3
- package/server/serverHelpers/registeredOperations.ts +79 -22
- package/server/serverHelpers/requestSanitization.ts +15 -0
- package/server/serverHelpers/serverHandlers.js +6 -3
- package/server/serverHelpers/serverUtilities.ts +232 -40
- package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
- package/server/serverHelpers/uwsServer.ts +17 -2
- package/server/static.ts +23 -29
- package/server/storageReclamation.ts +15 -2
- package/server/threads/isolatedApplications.ts +157 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/logRotationTransport.ts +40 -0
- package/server/threads/manageThreads.js +771 -66
- package/server/threads/socketRouter.ts +291 -30
- package/server/threads/threadHeapMemory.ts +26 -0
- package/server/threads/threadServer.js +73 -22
- package/sqlTranslator/index.ts +31 -8
- package/studio/web/assets/{Chat-4RrB5134.js → Chat-D3j-1yY1.js} +1 -1
- package/studio/web/assets/{FloatingChat-omlNMDcJ.js → FloatingChat-BxJGYcfB.js} +3 -3
- package/studio/web/assets/{apiToken-Bke3wvfZ.js → apiToken-CT55oWOe.js} +1 -1
- package/studio/web/assets/{applications-DvFDYJgK.js → applications-D9Ct9_vm.js} +1 -1
- package/studio/web/assets/{cssMode-C1vRa7zh.js → cssMode-DV8H7VwA.js} +1 -1
- package/studio/web/assets/{editor-3XRWEDWX.js → editor-uatc0unt.js} +1 -1
- package/studio/web/assets/{html-CTY5tdMr.js → html-Bm6D6paN.js} +1 -1
- package/studio/web/assets/{htmlMode-CG1vSD9t.js → htmlMode-CEn7tpLG.js} +1 -1
- package/studio/web/assets/{index-Dfpeofdu.js → index-BIXW6Pu4.js} +5 -5
- package/studio/web/assets/{index.lazy-B1VOIv-t.js → index.lazy-UI7L-Vrk.js} +1 -1
- package/studio/web/assets/{javascript-CJeJzGnI.js → javascript-CJ0G3AFZ.js} +1 -1
- package/studio/web/assets/{jsonMode-CYPBwM82.js → jsonMode-DQADAYEa.js} +1 -1
- package/studio/web/assets/{languageServices-IKH4GUHl.js → languageServices-CAQJXWcI.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-BC8_gxKG.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
- package/studio/web/assets/{notifications-BfKBpYcq.js → notifications-BbxTU6Aw.js} +1 -1
- package/studio/web/assets/{notifications-DFbArTfC.js → notifications-Cvb3P1lB.js} +1 -1
- package/studio/web/assets/{profile-Q4-T6c-S.js → profile-Yyb7gsvL.js} +1 -1
- package/studio/web/assets/{regions-CtkV0xje.js → regions-OgjGHlU5.js} +1 -1
- package/studio/web/assets/{register-1ZZuMsiA.js → register-6qwNEOY3.js} +2 -2
- package/studio/web/assets/{setComponentFile-ZWLcWv5X.js → setComponentFile-BilDMtgB.js} +1 -1
- package/studio/web/assets/{setup-DWprJyJy.js → setup-J6qJ7OIU.js} +2 -2
- package/studio/web/assets/{status--aNm8isn.js → status-0RWGcfyD.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-Cq_lt3XD.js → toggleHighContrast-BIn-vErT.js} +1 -1
- package/studio/web/assets/{tsMode-pjgytARx.js → tsMode-DgUXku4d.js} +1 -1
- package/studio/web/assets/{typescript-Cdg0mqUh.js → typescript-C9orXcsM.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-RZhaY8Rn.js → useEntityRestURL-BEoXXbUB.js} +1 -1
- package/studio/web/assets/{workers-BgoXIqQe.js → workers-JVzSDmgx.js} +1 -1
- package/studio/web/assets/{xml-BwSeDMiP.js → xml-Cq-S8S4X.js} +1 -1
- package/studio/web/assets/{yaml-DotCUG5l.js → yaml-sfoRdh1M.js} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-3-0.ts +132 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/OperationFunctionCaller.ts +2 -1
- package/utility/common_utils.ts +30 -5
- package/utility/componentNames.ts +12 -0
- package/utility/environment/environmentManager.ts +3 -7
- package/utility/environment/systemInformation.ts +7 -0
- package/utility/errors/commonErrors.ts +4 -0
- package/utility/errors/hdbError.ts +57 -0
- package/utility/hdbTerms.ts +19 -0
- package/utility/logging/harper_logger.ts +278 -26
- package/utility/logging/logGenerationCoordinator.ts +196 -0
- package/utility/logging/logRotation.ts +367 -0
- package/utility/logging/logRotator.ts +213 -81
- package/utility/nodeIdentity.ts +45 -0
- package/utility/npmUtilities.ts +12 -8
- package/utility/operationPermissions.ts +18 -1
- package/utility/operation_authorization.ts +231 -42
- package/utility/watchPath.ts +63 -0
- package/utility/watcherFallback.ts +148 -0
- package/validation/configValidator.ts +215 -75
- package/validation/installValidator.ts +15 -0
- package/validation/validationWrapper.ts +18 -4
|
@@ -1,19 +1,62 @@
|
|
|
1
1
|
const { isMainThread } = require('worker_threads');
|
|
2
2
|
const { getTables } = require('../resources/databases.ts');
|
|
3
|
-
const { loadComponentDirectories, loadComponent } = require('../components/componentLoader.ts');
|
|
3
|
+
const { loadComponentDirectories, loadComponent, readyComponentModules } = require('../components/componentLoader.ts');
|
|
4
4
|
const { resetResources } = require('../resources/Resources.ts');
|
|
5
5
|
const configUtils = require('../config/configUtils.ts');
|
|
6
6
|
const { dirname } = require('path');
|
|
7
7
|
const { loadCertificates } = require('../security/keys.ts');
|
|
8
|
-
const { installApplications } = require('../components/Application.ts');
|
|
8
|
+
const { installApplications, recoverInterruptedActivations } = require('../components/Application.ts');
|
|
9
9
|
const { errorForLog } = require('../utility/logging/harper_logger.ts');
|
|
10
|
+
const { CONFIG_PARAMS } = require('../utility/hdbTerms.ts');
|
|
10
11
|
|
|
11
12
|
let loadedComponents = new Map();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Map every component directory to the reason activation recovery could not run. Used only when the scan
|
|
16
|
+
* fails globally, where the safe answer is "none of these are known-good" rather than "all of these are".
|
|
17
|
+
*/
|
|
18
|
+
async function failEveryComponentClosed(cause) {
|
|
19
|
+
const failures = new Map();
|
|
20
|
+
const reason = cause instanceof Error ? cause : new Error(`Activation recovery could not run: ${String(cause)}`);
|
|
21
|
+
try {
|
|
22
|
+
const { readdir } = require('node:fs/promises');
|
|
23
|
+
const componentsRoot = configUtils.getConfigPath(CONFIG_PARAMS.COMPONENTSROOT);
|
|
24
|
+
for (const entry of await readdir(componentsRoot, { withFileTypes: true })) {
|
|
25
|
+
if (!entry.name.startsWith('.') && (entry.isDirectory() || entry.isSymbolicLink())) {
|
|
26
|
+
failures.set(entry.name, reason);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(errorForLog(error));
|
|
31
|
+
}
|
|
32
|
+
return failures;
|
|
33
|
+
}
|
|
12
34
|
/**
|
|
13
35
|
* This is main entry point for loading the main set of global server modules that power Harper.
|
|
14
36
|
* @returns {Promise<void>}
|
|
15
37
|
*/
|
|
16
38
|
async function loadRootComponents(isWorkerThread = false) {
|
|
39
|
+
// Interrupted activations are settled FIRST, before installApplications() — not merely before the
|
|
40
|
+
// component scan. installApplications() installs whatever the root config names, so a candidate left
|
|
41
|
+
// half-swapped by a crash has to be resolved before that runs, or boot reinstalls over it. Failures are
|
|
42
|
+
// per component and returned, not thrown: the loader fails those components closed and loads the rest.
|
|
43
|
+
// Undefined, not an empty Map: a worker skips this branch entirely, and handing the loader an empty map
|
|
44
|
+
// would assert "nothing is unreconciled" on a thread that never checked. The loader treats undefined as
|
|
45
|
+
// "no verdict from boot" instead.
|
|
46
|
+
let interruptedActivationFailures;
|
|
47
|
+
try {
|
|
48
|
+
if (isMainThread && !process.env.HARPER_SAFE_MODE) {
|
|
49
|
+
interruptedActivationFailures = await recoverInterruptedActivations(
|
|
50
|
+
configUtils.getConfigPath(CONFIG_PARAMS.COMPONENTSROOT)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
// The scan itself failed, so WHICH components are unsettled is unknown — loading them all would
|
|
55
|
+
// defeat the fail-closed contract this pass exists for. Every component present is failed closed
|
|
56
|
+
// instead, and a later reload cycle retries once the cause is gone.
|
|
57
|
+
console.error(errorForLog(error));
|
|
58
|
+
interruptedActivationFailures = await failEveryComponentClosed(error);
|
|
59
|
+
}
|
|
17
60
|
try {
|
|
18
61
|
if (isMainThread && !process.env.HARPER_SAFE_MODE) await installApplications();
|
|
19
62
|
} catch (error) {
|
|
@@ -33,13 +76,12 @@ async function loadRootComponents(isWorkerThread = false) {
|
|
|
33
76
|
});
|
|
34
77
|
if (!process.env.HARPER_SAFE_MODE) {
|
|
35
78
|
// once the global plugins are loaded, we now load all the CF and run applications (and their components)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (serverModule.ready) allReady.push(serverModule.ready());
|
|
79
|
+
const readyComponentPromises = new WeakMap();
|
|
80
|
+
await loadComponentDirectories(loadedComponents, resources, readyComponentPromises, interruptedActivationFailures);
|
|
81
|
+
await readyComponentModules(loadedComponents.keys(), readyComponentPromises);
|
|
82
|
+
return;
|
|
41
83
|
}
|
|
42
|
-
|
|
84
|
+
await readyComponentModules(loadedComponents.keys());
|
|
43
85
|
}
|
|
44
86
|
|
|
45
87
|
module.exports.loadRootComponents = loadRootComponents;
|
package/server/mqtt.ts
CHANGED
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
import { parser as makeParser, generate } from 'mqtt-packet';
|
|
4
4
|
import { getSession, DurableSubscriptionsSession } from './DurableSubscriptionsSession.ts';
|
|
5
5
|
import { getSuperUser } from '../security/user.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { getDeserializer } from './serverHelpers/contentTypes.ts';
|
|
7
|
+
import {
|
|
8
|
+
getSharedMessageEncoding,
|
|
9
|
+
getSharedFrame,
|
|
10
|
+
setSharedFrame,
|
|
11
|
+
resolveSharedPayload,
|
|
12
|
+
} from './serverHelpers/sharedMessageEncoding.ts';
|
|
7
13
|
import { recordAction, addAnalyticsListener, recordActionBinary } from '../resources/analytics/write.ts';
|
|
8
14
|
import { server } from '../server/Server.ts';
|
|
9
15
|
import { get } from '../utility/environment/environmentManager.ts';
|
|
@@ -12,6 +18,14 @@ import { loggerWithTag } from '../utility/logging/logger.ts';
|
|
|
12
18
|
import { forComponent as loggerForComponent } from '../utility/logging/harper_logger.ts';
|
|
13
19
|
import { EventEmitter } from 'events';
|
|
14
20
|
import { verifyCertificate } from '../security/certificateVerification/index.ts';
|
|
21
|
+
import { registerShutdownDrain } from '../components/shutdownDrain.ts';
|
|
22
|
+
import {
|
|
23
|
+
assertNoDeferredCredentialRejection,
|
|
24
|
+
getDeferredCredentialRejection,
|
|
25
|
+
} from '../security/deferredAuthentication.ts';
|
|
26
|
+
|
|
27
|
+
/** RFC 6455 private-use close code Harper already maps HTTP 401 to (see server/REST.ts). */
|
|
28
|
+
const WEBSOCKET_UNAUTHORIZED_CLOSE_CODE = 3000;
|
|
15
29
|
const authEventLog = loggerWithTag('auth-event');
|
|
16
30
|
const mqttLog = loggerForComponent('mqtt');
|
|
17
31
|
|
|
@@ -40,6 +54,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
40
54
|
};
|
|
41
55
|
// a no-op error handler to prevent unhandled error events from being rethrown
|
|
42
56
|
(server as any).mqtt.events.on('error', () => {});
|
|
57
|
+
registerShutdownDisconnect();
|
|
43
58
|
}
|
|
44
59
|
const mqttSettings = (server as any).mqtt;
|
|
45
60
|
function emitEvent(type: string, ...args: any[]) {
|
|
@@ -60,13 +75,29 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
60
75
|
|
|
61
76
|
emitEvent('connection', ws);
|
|
62
77
|
mqttLog.debug?.('Received WebSocket connection for MQTT from', ws._socket.remoteAddress);
|
|
78
|
+
// Both WebSocket entry points invoke this listener synchronously with the HTTP chain still
|
|
79
|
+
// pending (server/http.ts), so authentication has not recorded a credential rejection yet.
|
|
80
|
+
// It settles on the same promise the session principal comes from, which onSocket awaits
|
|
81
|
+
// before it processes any packet — the handlers below still attach synchronously, so no
|
|
82
|
+
// frame that arrives in the meantime is dropped.
|
|
83
|
+
const authenticated = Promise.resolve(chainCompletion).then(() => {
|
|
84
|
+
assertNoDeferredCredentialRejection(request);
|
|
85
|
+
return request?.user;
|
|
86
|
+
});
|
|
87
|
+
authenticated.catch((error) => {
|
|
88
|
+
mqttLog.info?.('Closing MQTT WebSocket connection, authentication was rejected', error);
|
|
89
|
+
ws.close(
|
|
90
|
+
WEBSOCKET_UNAUTHORIZED_CLOSE_CODE,
|
|
91
|
+
getDeferredCredentialRejection(request)?.message ?? 'Unauthorized'
|
|
92
|
+
);
|
|
93
|
+
});
|
|
63
94
|
const { onMessage, onClose } = onSocket(
|
|
64
95
|
ws,
|
|
65
96
|
(message) => {
|
|
66
97
|
ws.send(message);
|
|
67
98
|
},
|
|
68
99
|
request,
|
|
69
|
-
|
|
100
|
+
authenticated,
|
|
70
101
|
mqttSettings
|
|
71
102
|
);
|
|
72
103
|
ws.on('message', onMessage);
|
|
@@ -168,6 +199,50 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
168
199
|
);
|
|
169
200
|
}
|
|
170
201
|
}
|
|
202
|
+
const SERVER_SHUTTING_DOWN = 0x8b; // MQTT v5 DISCONNECT reason code
|
|
203
|
+
const REASON_STRING_LIMIT = 256; // bytes
|
|
204
|
+
/** Fixed header, message id and reason code, plus the property identifier and its length prefix. */
|
|
205
|
+
const ACK_PACKET_OVERHEAD = 16;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Every live MQTT connection on this worker, so a shutdown can announce itself: a bare socket close
|
|
209
|
+
* is indistinguishable to the client from a network failure.
|
|
210
|
+
*/
|
|
211
|
+
const liveConnections = new Set<{ protocolVersion: () => number; send: (data: any) => void; close: () => void }>();
|
|
212
|
+
let shutdownDisconnectRegistered = false;
|
|
213
|
+
|
|
214
|
+
/** v3.1.1 has no server-to-client DISCONNECT, so those connections are only closed. */
|
|
215
|
+
export function disconnectClientsForShutdown() {
|
|
216
|
+
if (liveConnections.size === 0) return;
|
|
217
|
+
const disconnectPacket = generate({ cmd: 'disconnect', reasonCode: SERVER_SHUTTING_DOWN } as any, {
|
|
218
|
+
protocolVersion: 5,
|
|
219
|
+
});
|
|
220
|
+
for (const connection of liveConnections) {
|
|
221
|
+
try {
|
|
222
|
+
if (connection.protocolVersion() >= 5) connection.send(disconnectPacket);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
mqttLog.debug?.('Could not notify MQTT connection of shutdown', error);
|
|
225
|
+
} finally {
|
|
226
|
+
try {
|
|
227
|
+
connection.close();
|
|
228
|
+
} catch (error) {
|
|
229
|
+
mqttLog.debug?.('Could not close MQTT connection during shutdown', error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
liveConnections.clear();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function registerShutdownDisconnect() {
|
|
237
|
+
if (shutdownDisconnectRegistered) return;
|
|
238
|
+
shutdownDisconnectRegistered = true;
|
|
239
|
+
registerShutdownDrain({
|
|
240
|
+
// Notifying clients is bounded and synchronous; it must never extend the shutdown deadline.
|
|
241
|
+
hasWork: () => false,
|
|
242
|
+
drain: async () => disconnectClientsForShutdown(),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
171
246
|
let addingMetrics,
|
|
172
247
|
numberOfConnections = 0;
|
|
173
248
|
function onSocket(socket, send, request, user, mqttSettings) {
|
|
@@ -191,7 +266,17 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
191
266
|
}
|
|
192
267
|
let disconnected;
|
|
193
268
|
numberOfConnections++;
|
|
269
|
+
const connection = {
|
|
270
|
+
protocolVersion: () => mqttOptions.protocolVersion,
|
|
271
|
+
send,
|
|
272
|
+
close: () => (socket.close ? socket.close() : socket.end()),
|
|
273
|
+
};
|
|
274
|
+
liveConnections.add(connection);
|
|
194
275
|
let session: DurableSubscriptionsSession;
|
|
276
|
+
// [MQTT-3.1.2-29]: a client that asks for no problem information must not be sent a reason
|
|
277
|
+
// string on a PUBACK/PUBREC, and [MQTT-3.1.2-24] caps what it will accept at all.
|
|
278
|
+
let sendProblemInformation = true;
|
|
279
|
+
let maximumPacketSize: number | undefined;
|
|
195
280
|
const mqttOptions = { protocolVersion: 4 };
|
|
196
281
|
const parser = makeParser({ protocolVersion: 5 });
|
|
197
282
|
function onMessage(data) {
|
|
@@ -199,6 +284,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
199
284
|
}
|
|
200
285
|
function onClose() {
|
|
201
286
|
numberOfConnections--;
|
|
287
|
+
liveConnections.delete(connection);
|
|
202
288
|
if (!disconnected) {
|
|
203
289
|
disconnected = true;
|
|
204
290
|
session?.disconnect?.(false);
|
|
@@ -236,6 +322,10 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
236
322
|
switch (command) {
|
|
237
323
|
case 'connect':
|
|
238
324
|
mqttOptions.protocolVersion = packet.protocolVersion;
|
|
325
|
+
// mqtt-packet parses this byte into a boolean, and a client may also send the raw 0.
|
|
326
|
+
const requestProblemInformation = packet.properties?.requestProblemInformation;
|
|
327
|
+
sendProblemInformation = requestProblemInformation !== false && requestProblemInformation !== 0;
|
|
328
|
+
maximumPacketSize = packet.properties?.maximumPacketSize;
|
|
239
329
|
if (packet.username) {
|
|
240
330
|
try {
|
|
241
331
|
user = await server.getUser(packet.username, packet.password.toString(), request);
|
|
@@ -260,7 +350,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
260
350
|
}
|
|
261
351
|
emitEvent('auth-failed', packet, socket, error);
|
|
262
352
|
recordActionBinary(false, 'connection', 'mqtt', 'connect');
|
|
263
|
-
return
|
|
353
|
+
return generateAndSendPacket({
|
|
264
354
|
// Send a connection acknowledgment with indication of auth failure
|
|
265
355
|
cmd: 'connack',
|
|
266
356
|
reasonCode: 0x04, // bad username or password, v3.1.1
|
|
@@ -271,7 +361,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
271
361
|
if (!user && mqttSettings.requireAuthentication) {
|
|
272
362
|
emitEvent('auth-failed', packet, socket);
|
|
273
363
|
recordActionBinary(false, 'connection', 'mqtt', 'connect');
|
|
274
|
-
return
|
|
364
|
+
return generateAndSendPacket({
|
|
275
365
|
// Send a connection acknowledgment with indication of auth failure
|
|
276
366
|
cmd: 'connack',
|
|
277
367
|
reasonCode: 0x04, // bad username or password, v3.1.1
|
|
@@ -307,7 +397,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
307
397
|
mqttLog.error?.(error);
|
|
308
398
|
emitEvent('auth-failed', packet, socket, error);
|
|
309
399
|
recordActionBinary(false, 'connection', 'mqtt', 'connect');
|
|
310
|
-
return
|
|
400
|
+
return generateAndSendPacket({
|
|
311
401
|
// Send a connection acknowledgment with indication of auth failure
|
|
312
402
|
cmd: 'connack',
|
|
313
403
|
reasonCode: error.code || 0x05,
|
|
@@ -316,28 +406,55 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
316
406
|
}
|
|
317
407
|
emitEvent('connected', session, socket);
|
|
318
408
|
recordActionBinary(true, 'connection', 'mqtt', 'connect');
|
|
319
|
-
|
|
409
|
+
generateAndSendPacket({
|
|
320
410
|
// Send a connection acknowledgment
|
|
321
411
|
cmd: 'connack',
|
|
322
412
|
sessionPresent: session.sessionWasPresent,
|
|
323
413
|
reasonCode: 0,
|
|
324
414
|
returnCode: 0, // success
|
|
325
415
|
});
|
|
326
|
-
const listener = async (topic, message, messageId, subscription) => {
|
|
416
|
+
const listener = async (topic, message, messageId, subscription, version) => {
|
|
327
417
|
try {
|
|
328
418
|
if (disconnected) throw new Error('Session disconnected while trying to send message to', topic);
|
|
329
419
|
const slashIndex = topic.indexOf('/', 1);
|
|
330
420
|
const generalTopic = slashIndex > 0 ? topic.slice(0, slashIndex) : topic;
|
|
331
|
-
|
|
332
|
-
|
|
421
|
+
const qos = subscription.qos || 0;
|
|
422
|
+
// Every subscriber of a topic serializes the same message to the same bytes, so the
|
|
423
|
+
// payload is encoded once per (message, content type) and reused across all of them.
|
|
424
|
+
const encoding = getSharedMessageEncoding(message, request, version);
|
|
425
|
+
const encoded = encoding.payload;
|
|
426
|
+
// only pay for a microtask when the serialization is genuinely still pending
|
|
427
|
+
const payload =
|
|
428
|
+
typeof (encoded as any)?.then === 'function'
|
|
429
|
+
? await resolveSharedPayload(encoding, message, request, version)
|
|
430
|
+
: (encoded as Buffer | string);
|
|
431
|
+
if (qos > 0) {
|
|
432
|
+
// mqtt-packet requires a numeric message identifier once qos is non-zero
|
|
433
|
+
const packetData: any = {
|
|
333
434
|
cmd: 'publish',
|
|
334
435
|
topic,
|
|
335
|
-
payload
|
|
436
|
+
payload,
|
|
336
437
|
messageId: messageId || Math.floor(Math.random() * 100000000),
|
|
337
|
-
qos
|
|
338
|
-
}
|
|
339
|
-
generalTopic
|
|
340
|
-
|
|
438
|
+
qos,
|
|
439
|
+
};
|
|
440
|
+
sendPacket(generate(packetData, mqttOptions), packetMethodName(packetData), generalTopic);
|
|
441
|
+
} else {
|
|
442
|
+
// A QoS 0 PUBLISH carries no message identifier, so the whole packet depends only on
|
|
443
|
+
// the payload, the topic, and the protocol version (v5 emits a properties field that
|
|
444
|
+
// v3.1.1 omits) — share it across every QoS 0 subscriber that matches on those.
|
|
445
|
+
// The frame also varies on dup/retain/properties, which are fixed below; anything
|
|
446
|
+
// that starts varying them per subscriber (RETAIN propagation, v5 subscription
|
|
447
|
+
// identifiers) has to join the key or subscribers will be served the wrong flags.
|
|
448
|
+
const protocolVersion = mqttOptions.protocolVersion;
|
|
449
|
+
let packet = getSharedFrame(encoding, protocolVersion, topic);
|
|
450
|
+
if (packet === undefined) {
|
|
451
|
+
packet = generate({ cmd: 'publish', topic, payload, qos: 0, dup: false, retain: false }, mqttOptions);
|
|
452
|
+
// only worth retaining once a second subscriber has shown up; caching a packet
|
|
453
|
+
// for a fan-out of one just pins a whole buffer nothing will read
|
|
454
|
+
if (encoding.hits > 0) setSharedFrame(encoding, protocolVersion, topic, packet);
|
|
455
|
+
}
|
|
456
|
+
sendPacket(packet, 'publish', generalTopic);
|
|
457
|
+
}
|
|
341
458
|
// wait if there is back-pressure
|
|
342
459
|
const rawSocket = socket._socket ?? socket;
|
|
343
460
|
if (rawSocket.writableNeedDrain) {
|
|
@@ -351,7 +468,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
351
468
|
return false;
|
|
352
469
|
}
|
|
353
470
|
};
|
|
354
|
-
session.setListener(listener
|
|
471
|
+
session.setListener(listener);
|
|
355
472
|
if (session.sessionWasPresent) await session.resume();
|
|
356
473
|
break;
|
|
357
474
|
case 'subscribe':
|
|
@@ -383,7 +500,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
383
500
|
granted.push(grantedQos);
|
|
384
501
|
}
|
|
385
502
|
await session.committed;
|
|
386
|
-
|
|
503
|
+
generateAndSendPacket({
|
|
387
504
|
// Send a subscription acknowledgment
|
|
388
505
|
cmd: 'suback',
|
|
389
506
|
granted,
|
|
@@ -395,7 +512,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
395
512
|
for (const subscription of packet.unsubscriptions) {
|
|
396
513
|
granted.push(session.removeSubscription(subscription) ? 0 : 17);
|
|
397
514
|
}
|
|
398
|
-
|
|
515
|
+
generateAndSendPacket({
|
|
399
516
|
// Send a subscription acknowledgment
|
|
400
517
|
cmd: 'unsuback',
|
|
401
518
|
granted,
|
|
@@ -404,7 +521,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
404
521
|
break;
|
|
405
522
|
}
|
|
406
523
|
case 'pubrel':
|
|
407
|
-
|
|
524
|
+
generateAndSendPacket({
|
|
408
525
|
// Send a publish response
|
|
409
526
|
cmd: 'pubcomp',
|
|
410
527
|
messageId: packet.messageId,
|
|
@@ -426,20 +543,44 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
426
543
|
emitEvent('error', error, socket, packet, session);
|
|
427
544
|
mqttLog.warn?.(error);
|
|
428
545
|
if (packet.qos > 0) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
546
|
+
// A publish to a topic no resource handles is the same miss addSubscription already
|
|
547
|
+
// answers with a specific code; reporting it as "unspecified error" leaves it
|
|
548
|
+
// indistinguishable from an internal failure.
|
|
549
|
+
const publishPacket: any = {
|
|
550
|
+
// Send a publish acknowledgment
|
|
551
|
+
cmd: responseCmd,
|
|
552
|
+
messageId: packet.messageId,
|
|
553
|
+
reasonCode:
|
|
554
|
+
mqttOptions.protocolVersion < 5
|
|
555
|
+
? 0x80 // the only error code in v3.1.1
|
|
556
|
+
: error?.statusCode === 403
|
|
557
|
+
? 0x87 // not authorized
|
|
558
|
+
: error?.statusCode === 404
|
|
559
|
+
? 0x90 // topic name invalid
|
|
560
|
+
: 0x80, // unspecified error
|
|
561
|
+
};
|
|
562
|
+
// Only errors this layer maps to a code of their own are safe to describe: any other
|
|
563
|
+
// failure carries an internal message that a client must not be handed. Both limits
|
|
564
|
+
// are in encoded bytes, so measure and trim the string in bytes too.
|
|
565
|
+
const describable =
|
|
566
|
+
mqttOptions.protocolVersion >= 5 &&
|
|
567
|
+
sendProblemInformation &&
|
|
568
|
+
error?.message &&
|
|
569
|
+
(error.statusCode === 403 || error.statusCode === 404);
|
|
570
|
+
const reasonString = describable
|
|
571
|
+
? Buffer.from(String(error.message), 'utf8').subarray(0, REASON_STRING_LIMIT).toString('utf8')
|
|
572
|
+
: undefined;
|
|
573
|
+
if (
|
|
574
|
+
reasonString &&
|
|
575
|
+
(!maximumPacketSize || maximumPacketSize >= Buffer.byteLength(reasonString) + ACK_PACKET_OVERHEAD)
|
|
576
|
+
)
|
|
577
|
+
publishPacket.properties = { reasonString };
|
|
578
|
+
generateAndSendPacket(publishPacket, packet.topic);
|
|
438
579
|
}
|
|
439
580
|
break;
|
|
440
581
|
}
|
|
441
582
|
if (packet.qos > 0) {
|
|
442
|
-
|
|
583
|
+
generateAndSendPacket(
|
|
443
584
|
{
|
|
444
585
|
// Send a publish acknowledgment
|
|
445
586
|
cmd: responseCmd,
|
|
@@ -454,7 +595,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
454
595
|
}
|
|
455
596
|
break;
|
|
456
597
|
case 'pubrec':
|
|
457
|
-
|
|
598
|
+
generateAndSendPacket({
|
|
458
599
|
// Send a publish response
|
|
459
600
|
cmd: 'pubrel',
|
|
460
601
|
messageId: packet.messageId,
|
|
@@ -467,7 +608,7 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
467
608
|
emitEvent('acknowledged', session, packet);
|
|
468
609
|
break;
|
|
469
610
|
case 'pingreq':
|
|
470
|
-
|
|
611
|
+
generateAndSendPacket({ cmd: 'pingresp' });
|
|
471
612
|
break;
|
|
472
613
|
case 'disconnect':
|
|
473
614
|
disconnected = true;
|
|
@@ -483,22 +624,22 @@ function onSocket(socket, send, request, user, mqttSettings) {
|
|
|
483
624
|
} catch (error) {
|
|
484
625
|
emitEvent('error', error, socket, packet, session);
|
|
485
626
|
mqttLog.error?.(error);
|
|
486
|
-
|
|
627
|
+
generateAndSendPacket({
|
|
487
628
|
// Send a subscription acknowledgment
|
|
488
629
|
cmd: 'disconnect',
|
|
489
630
|
});
|
|
490
631
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
send(
|
|
494
|
-
recordAction(
|
|
632
|
+
// analytics stay per subscriber even when the packet itself is shared across them
|
|
633
|
+
function sendPacket(packet, methodName, path?) {
|
|
634
|
+
send(packet);
|
|
635
|
+
recordAction(packet.length, 'bytes-sent', path, methodName, 'mqtt');
|
|
636
|
+
}
|
|
637
|
+
function generateAndSendPacket(packetData, path?) {
|
|
638
|
+
sendPacket(generate(packetData, mqttOptions), packetMethodName(packetData), path);
|
|
495
639
|
}
|
|
496
640
|
function packetMethodName(packet) {
|
|
497
641
|
return packet.qos > 0 ? packet.cmd + ',qos=' + packet.qos : packet.cmd;
|
|
498
642
|
}
|
|
499
|
-
function serialize(data) {
|
|
500
|
-
return serializeMessage(data, request);
|
|
501
|
-
}
|
|
502
643
|
});
|
|
503
644
|
parser.on('error', (error) => {
|
|
504
645
|
mqttLog.warn('MQTT parsing error, closing connection:', error.message);
|
package/server/nodeName.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { X509Certificate } from 'node:crypto';
|
|
3
|
+
import { isIP } from 'node:net';
|
|
3
4
|
import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
|
|
4
5
|
import * as env from '../utility/environment/environmentManager.ts';
|
|
5
6
|
import { logger } from '../utility/logging/logger.ts';
|
|
7
|
+
import { bareHostViolation } from '../utility/nodeIdentity.ts';
|
|
6
8
|
import { server } from './Server.ts';
|
|
7
9
|
|
|
8
10
|
Object.defineProperty(server, 'hostname', {
|
|
@@ -17,20 +19,26 @@ function getCommonNameFromCert() {
|
|
|
17
19
|
const certificatePath: string | undefined =
|
|
18
20
|
env.get(CONFIG_PARAMS.OPERATIONSAPI_TLS_CERTIFICATE) || env.get(CONFIG_PARAMS.TLS_CERTIFICATE);
|
|
19
21
|
if (certificatePath) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
try {
|
|
23
|
+
// use this to get the hostname if it isn't provided by config
|
|
24
|
+
const subject = new X509Certificate(readFileSync(certificatePath)).subject;
|
|
25
|
+
return (commonNameFromCert = subject?.match(/CN=(.*)/)?.[1] ?? null);
|
|
26
|
+
} catch {
|
|
27
|
+
// a missing/unparseable cert file must not throw out of identity resolution — the caller
|
|
28
|
+
// falls back to another source. Do NOT cache the failure, so a later read (e.g. after the
|
|
29
|
+
// cert is created) can still pick up its common name.
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
24
32
|
}
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
let nodeName: string | undefined;
|
|
28
36
|
export function getThisNodeName(): string {
|
|
29
37
|
if (nodeName) return nodeName; // if already determined, just return
|
|
30
|
-
|
|
31
|
-
if (
|
|
38
|
+
const configured = env.get(CONFIG_PARAMS.NODE_HOSTNAME);
|
|
39
|
+
if (configured) {
|
|
32
40
|
const replicationHostname = env.get('replication_hostname');
|
|
33
|
-
if (replicationHostname && replicationHostname !==
|
|
41
|
+
if (replicationHostname && replicationHostname !== configured) {
|
|
34
42
|
// If these are both set and differ, the node identity is ambiguous. node.hostname
|
|
35
43
|
// wins (it is what this node identifies as), but if it doesn't match the name this
|
|
36
44
|
// node is registered under in hdb_nodes, replication for that name silently turns
|
|
@@ -38,18 +46,21 @@ export function getThisNodeName(): string {
|
|
|
38
46
|
// node.hostname value — that's how a wrong identity (e.g. 'localhost') gets locked
|
|
39
47
|
// in. Steer the operator to reconcile against the registered node name instead.
|
|
40
48
|
logger.warn?.(
|
|
41
|
-
`The node.hostname (${
|
|
49
|
+
`The node.hostname (${configured}) and replication.hostname (${replicationHostname}) configuration values are both set and differ. This node will identify as "${configured}". Ensure that name matches this node's row in system.hdb_nodes; if it does not, set node.hostname (or remove it to fall back to replication.hostname) to match the registered node name, otherwise replication for this node will be disabled.`
|
|
42
50
|
);
|
|
43
51
|
}
|
|
44
|
-
return nodeName;
|
|
45
52
|
}
|
|
46
|
-
//
|
|
53
|
+
// Use the first source that is a valid bare host (harper#2218). node.hostname and
|
|
54
|
+
// replication.hostname are rejected at the config boundary when invalid; the remaining derived
|
|
55
|
+
// sources are validated here and skipped — not fatal — if unusable, so a descriptive certificate
|
|
56
|
+
// common name or an empty value can never cache a corrupt identity or crash a request-time caller.
|
|
47
57
|
nodeName =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
getHostFromListeningPort('
|
|
58
|
+
asBareHost(configured, 'node.hostname') ??
|
|
59
|
+
asBareHost(env.get('replication_hostname'), 'replication.hostname') ?? // for backwards compatibility
|
|
60
|
+
asBareHost(replicationUrlHost(), 'the host in replication.url') ??
|
|
61
|
+
asBareHost(getCommonNameFromCert(), 'the certificate common name') ??
|
|
62
|
+
asBareHost(getHostFromListeningPort('operationsapi_network_secureport'), 'the operations API host') ??
|
|
63
|
+
asBareHost(getHostFromListeningPort('operationsapi_network_port'), 'the operations API host') ??
|
|
53
64
|
'127.0.0.1';
|
|
54
65
|
return nodeName;
|
|
55
66
|
}
|
|
@@ -58,10 +69,66 @@ export function clearThisNodeName() {
|
|
|
58
69
|
nodeName = undefined;
|
|
59
70
|
}
|
|
60
71
|
|
|
72
|
+
// Reduce a node name to a host safe to compose a display URL from: strip a scheme/port (defensive —
|
|
73
|
+
// a configured node.hostname carrying those is now rejected at the config boundary) and bracket a
|
|
74
|
+
// bare IPv6 literal, without which the composed startup URLs are unparseable.
|
|
75
|
+
export function nodeNameToDisplayHost(name: string): string {
|
|
76
|
+
if (!name) return name;
|
|
77
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(name);
|
|
78
|
+
const candidates = hasScheme ? [name] : [`http://${name}`, `http://[${name}]`];
|
|
79
|
+
for (const candidate of candidates) {
|
|
80
|
+
try {
|
|
81
|
+
const { hostname } = new URL(candidate);
|
|
82
|
+
if (hostname) return hostname;
|
|
83
|
+
} catch {}
|
|
84
|
+
}
|
|
85
|
+
return name;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getThisNodeHostname(): string {
|
|
89
|
+
return nodeNameToDisplayHost(getThisNodeName());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The host of replication.url, or undefined when it is unset or carries no usable host. Warns in the
|
|
94
|
+
* latter case for the same reason asBareHost does: a typo'd URL must not silently cost this node its
|
|
95
|
+
* identity. urlToNodeName itself stays quiet — it is also used for peer URLs, not just this node's.
|
|
96
|
+
*/
|
|
97
|
+
function replicationUrlHost(): string | undefined {
|
|
98
|
+
const url: string | undefined = env.get(CONFIG_PARAMS.REPLICATION_URL);
|
|
99
|
+
if (!url) return undefined;
|
|
100
|
+
const host = urlToNodeName(url);
|
|
101
|
+
if (host === undefined) {
|
|
102
|
+
logger.warn?.(
|
|
103
|
+
`Ignoring replication.url (${JSON.stringify(url)}) as this node's identity: it is not a URL with a host. Falling back to the next available source, which may leave this node identifying as 127.0.0.1 and unable to replicate.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return host;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A candidate identity source, or undefined when it is unset or not a bare host. Skipping is
|
|
111
|
+
* deliberate (see getThisNodeName) but silence is a diagnosis trap: a typo'd source that quietly
|
|
112
|
+
* resolves identity to 127.0.0.1 looks like a working node until replication fails, so say so.
|
|
113
|
+
*/
|
|
114
|
+
function asBareHost(value: unknown, source: string): string | undefined {
|
|
115
|
+
if (value === undefined || value === null || value === '') return undefined;
|
|
116
|
+
const violation = bareHostViolation(value);
|
|
117
|
+
if (!violation) return value as string;
|
|
118
|
+
logger.warn?.(
|
|
119
|
+
`Ignoring ${source} (${JSON.stringify(value)}) as this node's identity: it ${violation}. Falling back to the next available source, which may leave this node identifying as 127.0.0.1 and unable to replicate. Set it to a bare hostname or IP literal.`
|
|
120
|
+
);
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
61
124
|
function getHostFromListeningPort(key: string) {
|
|
62
125
|
const port: string | undefined = env.get(key);
|
|
63
126
|
const lastColon = port?.lastIndexOf?.(':');
|
|
64
|
-
if (lastColon > 0)
|
|
127
|
+
if (lastColon > 0) {
|
|
128
|
+
const host = port.slice(0, lastColon);
|
|
129
|
+
// A listen address stores IPv6 bracketed ("[::1]:9925"); the node identity is unbracketed.
|
|
130
|
+
return host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
|
|
131
|
+
}
|
|
65
132
|
}
|
|
66
133
|
function getPortFromListeningPort(key: string) {
|
|
67
134
|
const port: string | undefined = env.get(key);
|
|
@@ -72,18 +139,33 @@ function getPortFromListeningPort(key: string) {
|
|
|
72
139
|
|
|
73
140
|
export function hostnameToUrl(hostname) {
|
|
74
141
|
if (!hostname) return undefined;
|
|
142
|
+
// A bare IPv6 literal must be bracketed for the authority to parse: "ws://::1:9925" is rejected by
|
|
143
|
+
// the URL parser, "ws://[::1]:9925" is not. Hostnames and IPv4 pass through unchanged.
|
|
144
|
+
const host = isIP(hostname) === 6 ? `[${hostname}]` : hostname;
|
|
75
145
|
let port = getPortFromListeningPort('replication_port');
|
|
76
|
-
if (port) return `ws://${
|
|
146
|
+
if (port) return `ws://${host}:${port}`;
|
|
77
147
|
port = getPortFromListeningPort('replication_secureport');
|
|
78
|
-
if (port) return `wss://${
|
|
148
|
+
if (port) return `wss://${host}:${port}`;
|
|
79
149
|
port = getPortFromListeningPort('operationsapi_network_port');
|
|
80
|
-
if (port) return `ws://${
|
|
150
|
+
if (port) return `ws://${host}:${port}`;
|
|
81
151
|
port = getPortFromListeningPort('operationsapi_network_secureport');
|
|
82
|
-
if (port) return `wss://${
|
|
152
|
+
if (port) return `wss://${host}:${port}`;
|
|
83
153
|
}
|
|
84
154
|
|
|
85
155
|
export function urlToNodeName(nodeUrl?: string | URL): string | undefined {
|
|
86
|
-
if (nodeUrl) return
|
|
156
|
+
if (!nodeUrl) return undefined;
|
|
157
|
+
try {
|
|
158
|
+
// the part of the URL that is the node name, matched against the certificate common name. URL
|
|
159
|
+
// keeps an IPv6 host bracketed ("[::1]"); the identity is stored unbracketed so it stays one
|
|
160
|
+
// canonical string across sources and net.isIP types it as an IP certificate SAN.
|
|
161
|
+
const host = new URL(nodeUrl).hostname;
|
|
162
|
+
// A hostless scheme ("mailto:", "data:", "file:", "urn:") parses but has an empty hostname —
|
|
163
|
+
// that is no name at all, not an identity, so treat it like a malformed URL.
|
|
164
|
+
if (host === '') return undefined;
|
|
165
|
+
return host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
|
|
166
|
+
} catch {
|
|
167
|
+
return undefined; // a malformed URL yields no name — callers skip this source rather than crash
|
|
168
|
+
}
|
|
87
169
|
}
|
|
88
170
|
|
|
89
171
|
export function getThisNodeUrl() {
|