@harperfast/harper 5.2.10 → 5.3.0-alpha.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 +46 -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 +59 -3
- package/bin/run.ts +6 -11
- package/bin/upgrade.js +7 -3
- package/bin/workloadIdentity.ts +119 -0
- package/components/Application.ts +2414 -242
- package/components/ApplicationScope.ts +8 -0
- package/components/EntryHandler.ts +59 -39
- package/components/OptionsWatcher.ts +150 -74
- package/components/RuntimeModuleTracker.ts +38 -7
- package/components/Scope.ts +37 -15
- package/components/awaitRestart.ts +84 -0
- package/components/componentLoader.ts +340 -30
- package/components/componentPreparationLock.ts +16 -5
- 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 +270 -78
- package/components/operationsValidation.js +49 -1
- package/components/status/ComponentStatusRegistry.ts +59 -0
- package/config/RootConfigWatcher.ts +80 -34
- package/config/configUtils.ts +291 -34
- package/config/harperConfigEnvVars.ts +170 -27
- package/config-root.schema.json +29 -0
- package/dataLayer/blobBackup.ts +160 -50
- package/dataLayer/delete.ts +6 -1
- package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
- 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 +48 -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 +54 -5
- 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 +139 -16
- package/dist/components/Application.js +2215 -267
- 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 +1 -0
- package/dist/components/OptionsWatcher.js +141 -74
- 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 +38 -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 +279 -22
- 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/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.js +231 -77
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +49 -1
- package/dist/components/operationsValidation.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 +7 -1
- package/dist/config/RootConfigWatcher.js +64 -27
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configUtils.d.ts +9 -1
- package/dist/config/configUtils.js +254 -33
- 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/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 +51 -10
- 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 +1 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +52 -0
- package/dist/resources/DatabaseTransaction.d.ts +55 -3
- package/dist/resources/DatabaseTransaction.js +460 -134
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.js +22 -4
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
- package/dist/resources/PrimaryRocksDatabase.js +30 -2
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +20 -0
- package/dist/resources/RecordEncoder.js +94 -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 +20 -11
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +20 -1
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/RocksIndexStore.d.ts +6 -1
- package/dist/resources/RocksIndexStore.js +24 -10
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
- package/dist/resources/RocksTransactionLogStore.js +57 -17
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +112 -8
- package/dist/resources/Table.js +1071 -197
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.d.ts +11 -2
- package/dist/resources/auditStore.js +183 -18
- 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/databases.d.ts +150 -9
- package/dist/resources/databases.js +1247 -525
- 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/graphql.d.ts +1 -1
- package/dist/resources/graphql.js +50 -15
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- 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/recordLock.d.ts +80 -0
- package/dist/resources/recordLock.js +184 -0
- package/dist/resources/recordLock.js.map +1 -0
- package/dist/resources/replayLogs.d.ts +14 -1
- package/dist/resources/replayLogs.js +152 -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/search.js +158 -32
- package/dist/resources/search.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 +75 -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 +73 -0
- 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 +11 -0
- package/dist/server/http.js +88 -15
- 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 +25 -0
- package/dist/server/serverHelpers/Headers.js +139 -1
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
- package/dist/server/serverHelpers/contentTypes.js +32 -25
- 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 +90 -20
- 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 +16 -0
- package/dist/server/storageReclamation.js.map +1 -1
- 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/manageThreads.d.ts +26 -4
- package/dist/server/threads/manageThreads.js +517 -72
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.js +89 -26
- 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 +46 -15
- 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 +17 -0
- package/dist/utility/errors/hdbError.js +30 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +16 -0
- package/dist/utility/hdbTerms.js +18 -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 +91 -14
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logRotator.js +76 -49
- 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 +9 -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 +86 -0
- package/dist/utility/watcherFallback.js +278 -1
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.d.ts +12 -0
- package/dist/validation/configValidator.js +199 -72
- 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 +2 -0
- package/json/systemSchema.json +52 -0
- package/npm-shrinkwrap.json +235 -233
- package/package.json +8 -7
- package/resources/DESIGN.md +114 -52
- package/resources/DatabaseTransaction.ts +522 -130
- package/resources/LMDBTransaction.ts +26 -4
- package/resources/PrimaryRocksDatabase.ts +29 -3
- package/resources/RecordEncoder.ts +103 -8
- package/resources/RequestTarget.ts +2 -0
- package/resources/Resource.ts +17 -9
- package/resources/ResourceInterface.ts +23 -0
- package/resources/RocksIndexStore.ts +30 -10
- package/resources/RocksTransactionLogStore.ts +78 -21
- package/resources/Table.ts +1107 -164
- package/resources/auditStore.ts +187 -21
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/databases.ts +1354 -522
- package/resources/defineTable.ts +18 -2
- package/resources/graphql.ts +70 -16
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
- package/resources/longLivedTransactions.ts +360 -0
- package/resources/models/backendRegistry.ts +84 -2
- package/resources/models/bootstrap.ts +473 -28
- package/resources/recordLock.ts +253 -0
- package/resources/replayLogs.ts +147 -26
- package/resources/replayLogsGuards.ts +171 -8
- package/resources/search.ts +154 -32
- 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 +81 -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 +183 -16
- package/server/DurableSubscriptionsSession.ts +71 -11
- package/server/REST.ts +79 -1
- package/server/graphqlQuerying.ts +4 -0
- package/server/http.ts +99 -18
- 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 +131 -0
- package/server/serverHelpers/contentTypes.ts +29 -21
- 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 +104 -31
- package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
- package/server/serverHelpers/uwsServer.ts +17 -2
- package/server/static.ts +23 -29
- package/server/storageReclamation.ts +13 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/manageThreads.js +526 -63
- package/server/threads/socketRouter.ts +81 -26
- package/server/threads/threadHeapMemory.ts +26 -0
- package/server/threads/threadServer.js +43 -15
- package/sqlTranslator/index.ts +31 -8
- package/studio/web/assets/{Chat-Br06zdMA.js → Chat-BnCBegQz.js} +1 -1
- package/studio/web/assets/{FloatingChat-BWImX5fA.js → FloatingChat-CoDW1ySS.js} +3 -3
- package/studio/web/assets/{apiToken-DN0nmDsq.js → apiToken-Bwk5BLXW.js} +1 -1
- package/studio/web/assets/{applications-kSxVoyeU.js → applications-DHxGi7JH.js} +1 -1
- package/studio/web/assets/{cssMode-C1JeufH5.js → cssMode-s0cWI-_M.js} +1 -1
- package/studio/web/assets/{editor-qoo9CrGO.js → editor-DNcRHK54.js} +1 -1
- package/studio/web/assets/{html-Dt4IIy04.js → html-Bdssedlg.js} +1 -1
- package/studio/web/assets/{htmlMode-DXgKKr4C.js → htmlMode-CoDlJ3fw.js} +1 -1
- package/studio/web/assets/{index-6onkYFOG.js → index-D6sxmFLR.js} +5 -5
- package/studio/web/assets/{index.lazy-BrCFnpNJ.js → index.lazy-tmU5BS8s.js} +1 -1
- package/studio/web/assets/{javascript-DNCQGUBc.js → javascript-B8meVSTH.js} +1 -1
- package/studio/web/assets/{jsonMode-CR6HWruP.js → jsonMode-DpIPd35T.js} +1 -1
- package/studio/web/assets/{languageServices-BM4fI4rS.js → languageServices-C_5FMJzQ.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-DSa1ttcD.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
- package/studio/web/assets/{notifications-BHXLnh6x.js → notifications-CQf18QKb.js} +1 -1
- package/studio/web/assets/{notifications-CUmtIA6z.js → notifications-CvZivSbh.js} +1 -1
- package/studio/web/assets/{profile-8BeFSF3j.js → profile-DdOwtntb.js} +1 -1
- package/studio/web/assets/{regions-C8qR0HhD.js → regions-n69fwagr.js} +1 -1
- package/studio/web/assets/{register-B4n5i0SD.js → register-PfWTCXWB.js} +2 -2
- package/studio/web/assets/{setComponentFile-g0_B0lgX.js → setComponentFile-Bg6O7X0S.js} +1 -1
- package/studio/web/assets/{setup-B0CTj_Q5.js → setup-CUx_aUDl.js} +2 -2
- package/studio/web/assets/{status-C6Yib7-K.js → status-D7BVKqX9.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-Dgta7bVi.js → toggleHighContrast-DBSyXzMr.js} +1 -1
- package/studio/web/assets/{tsMode-CH_jHvU-.js → tsMode-BByKCjBS.js} +1 -1
- package/studio/web/assets/{typescript-Co9LCXd5.js → typescript-DDLnLpw9.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-wKC8NsC_.js → useEntityRestURL-31CHGaHk.js} +1 -1
- package/studio/web/assets/{workers-CWeLxCXA.js → workers-pR3jRY9D.js} +1 -1
- package/studio/web/assets/{xml-BadC-0Rk.js → xml-2iRnMhQO.js} +1 -1
- package/studio/web/assets/{yaml-BiUfxPbC.js → yaml-Bf92gJpd.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 +29 -0
- package/utility/hdbTerms.ts +17 -0
- package/utility/logging/harper_logger.ts +87 -14
- package/utility/logging/logRotator.ts +72 -45
- package/utility/nodeIdentity.ts +45 -0
- package/utility/npmUtilities.ts +10 -8
- package/utility/operationPermissions.ts +18 -1
- package/utility/operation_authorization.ts +231 -42
- package/utility/watchPath.ts +63 -0
- package/utility/watcherFallback.ts +270 -0
- package/validation/configValidator.ts +209 -72
- package/validation/installValidator.ts +15 -0
- package/validation/validationWrapper.ts +18 -4
package/security/keys.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import { watch } from 'chokidar';
|
|
5
4
|
import * as fs from 'fs-extra';
|
|
6
5
|
import * as forge from 'node-forge';
|
|
7
6
|
import * as net from 'net';
|
|
@@ -33,7 +32,16 @@ export const getPrivateKeys = () => privateKeys;
|
|
|
33
32
|
import { readFileSync, statSync } from 'node:fs';
|
|
34
33
|
import { getTicketKeys, onMessageFromWorkers } from '../server/threads/manageThreads.js';
|
|
35
34
|
import { isMainThread } from 'worker_threads';
|
|
35
|
+
import {
|
|
36
|
+
POLLING_FALLBACK_OPTIONS,
|
|
37
|
+
claimLostNativeWatchError,
|
|
38
|
+
guardedWatch,
|
|
39
|
+
isWatcherExhaustionError,
|
|
40
|
+
warnWatcherFallback,
|
|
41
|
+
} from '../utility/watcherFallback.ts';
|
|
42
|
+
import { resolveWatchTarget } from '../utility/watchPath.ts';
|
|
36
43
|
import { TLSSocket } from 'node:tls';
|
|
44
|
+
import { publishTrustedAuthorities } from './certificateVerification/trustedIssuers.ts';
|
|
37
45
|
|
|
38
46
|
const CERT_VALIDITY_DAYS = 3650;
|
|
39
47
|
// Default interval (ms) for the periodic cert-file re-read safety net. The chokidar (inotify)
|
|
@@ -149,6 +157,12 @@ const privateKeys = new Map();
|
|
|
149
157
|
// subscription and the private-key hot-reload trigger so both coalesce on the same cadence.
|
|
150
158
|
const TLS_REBUILD_DEBOUNCE_MS = 1500;
|
|
151
159
|
|
|
160
|
+
// Self-retry backoff cap: a permanently bad record must not cost every selector on every
|
|
161
|
+
// thread a table scan + X509 parse per debounce interval, forever.
|
|
162
|
+
const TLS_FAILURE_RETRY_MAX_DELAY_MS = 300_000;
|
|
163
|
+
// While a failure signature is unchanged, repeat occurrences log a summary at most this often.
|
|
164
|
+
const TLS_FAILURE_SUMMARY_INTERVAL_MS = 3_600_000;
|
|
165
|
+
|
|
152
166
|
// Debounced rebuild triggers, one per live server TLS selector (registered in createTLSSelector's
|
|
153
167
|
// initialize). When a private key is hot-reloaded on this thread, every live selector re-runs
|
|
154
168
|
// updateTLS so a secure context built with a stale key — or built before this key arrived — is
|
|
@@ -269,7 +283,9 @@ export function loadCertificates() {
|
|
|
269
283
|
return;
|
|
270
284
|
}
|
|
271
285
|
|
|
272
|
-
|
|
286
|
+
// Returned so loadAndWatch can roll back its mtime latch if the write fails —
|
|
287
|
+
// assigned as well to preserve loadCertificates()'s awaited-return contract.
|
|
288
|
+
return (promise = certificateTable.put({
|
|
273
289
|
name: certCn,
|
|
274
290
|
uses: config.uses ?? (configKey.includes('operations') ? ['operations-api'] : []),
|
|
275
291
|
ciphers: config.ciphers,
|
|
@@ -286,7 +302,7 @@ export function loadCertificates() {
|
|
|
286
302
|
valid_from: x509Cert.validFrom,
|
|
287
303
|
valid_to: x509Cert.validTo,
|
|
288
304
|
},
|
|
289
|
-
});
|
|
305
|
+
}));
|
|
290
306
|
},
|
|
291
307
|
ca ? 'certificate authority' : 'certificate'
|
|
292
308
|
);
|
|
@@ -338,24 +354,65 @@ const certificateWatchPollers = new Map<string, () => void>();
|
|
|
338
354
|
*/
|
|
339
355
|
function loadAndWatch(path, loadCert, type) {
|
|
340
356
|
let lastModified;
|
|
341
|
-
const loadFile = (path, stats) => {
|
|
357
|
+
const loadFile = (path, stats?) => {
|
|
358
|
+
// The latch dedupes chokidar/poll but must mean "last successfully APPLIED", or a failed
|
|
359
|
+
// apply is deduplicated forever (#2382). Rollbacks are equality-guarded so an old failure
|
|
360
|
+
// can't unlatch a newer successful reload.
|
|
361
|
+
const previousModified = lastModified;
|
|
362
|
+
let modified;
|
|
342
363
|
try {
|
|
343
364
|
// chokidar's 'change' event omits stats unless alwaysStat is set (default off in v4), so
|
|
344
365
|
// stat the file here when it's missing — otherwise the inotify fast path would throw and
|
|
345
366
|
// silently never reload, leaving only the periodic poll to catch the change.
|
|
346
|
-
|
|
367
|
+
modified = (stats ?? statSync(path)).mtimeMs;
|
|
347
368
|
if (modified && modified !== lastModified) {
|
|
348
369
|
if (lastModified && isMainThread) logger.warn?.(`Reloading ${type}:`, path);
|
|
349
370
|
lastModified = modified;
|
|
350
|
-
loadCert(readPEM(path));
|
|
371
|
+
const applied = loadCert(readPEM(path));
|
|
372
|
+
if (typeof (applied as any)?.then === 'function') {
|
|
373
|
+
(applied as Promise<unknown>).catch((error) => {
|
|
374
|
+
logger.error?.(`Error applying ${type}:`, path, error);
|
|
375
|
+
if (lastModified === modified) lastModified = previousModified;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
351
378
|
}
|
|
352
379
|
} catch (error) {
|
|
353
380
|
logger.error?.(`Error loading ${type}:`, path, error);
|
|
381
|
+
if (modified !== undefined && lastModified === modified) lastModified = previousModified;
|
|
354
382
|
}
|
|
355
383
|
};
|
|
356
384
|
if (fs.existsSync(path)) loadFile(path, statSync(path));
|
|
357
385
|
else logger.error?.(`${type} file not found:`, path);
|
|
358
|
-
|
|
386
|
+
const watchTarget = resolveWatchTarget(path);
|
|
387
|
+
let usingPolling = watchTarget.mustPoll;
|
|
388
|
+
let liveWatcher;
|
|
389
|
+
const openWatcher = () => {
|
|
390
|
+
const opened = (liveWatcher = guardedWatch(watchTarget.path, {
|
|
391
|
+
persistent: false,
|
|
392
|
+
...(usingPolling ? POLLING_FALLBACK_OPTIONS : {}),
|
|
393
|
+
}));
|
|
394
|
+
opened
|
|
395
|
+
// The event carries the watched spelling, which is not the configured one once canonicalized;
|
|
396
|
+
// reload through the configured path so a retargeted link is followed.
|
|
397
|
+
.on('change', () => loadFile(path))
|
|
398
|
+
// chokidar emits 'error' unguarded for anything but ENOENT/ENOTDIR.
|
|
399
|
+
.on('error', (error) => {
|
|
400
|
+
if (claimLostNativeWatchError(error)) return;
|
|
401
|
+
if (isWatcherExhaustionError(error)) {
|
|
402
|
+
if (usingPolling || liveWatcher !== opened) return;
|
|
403
|
+
warnWatcherFallback(path);
|
|
404
|
+
usingPolling = true;
|
|
405
|
+
Promise.resolve()
|
|
406
|
+
.then(() => opened.close())
|
|
407
|
+
.catch(() => {})
|
|
408
|
+
.then(openWatcher)
|
|
409
|
+
.catch((error) => logger.error?.(`Could not reopen the ${type} watch on polling:`, path, error));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
logger.error?.(`Error watching ${type}:`, path, error);
|
|
413
|
+
});
|
|
414
|
+
};
|
|
415
|
+
openWatcher();
|
|
359
416
|
|
|
360
417
|
// Periodic re-read safety net. For certificates, this runs on the main thread only — workers
|
|
361
418
|
// receive cert updates via the hdb_certificate table subscription, so polling the cert file on
|
|
@@ -392,13 +449,15 @@ function loadAndWatch(path, loadCert, type) {
|
|
|
392
449
|
}
|
|
393
450
|
|
|
394
451
|
function getHost() {
|
|
395
|
-
|
|
396
|
-
|
|
452
|
+
// urlToNodeName returns undefined for a missing or malformed node/replication URL, so a bad
|
|
453
|
+
// replication.url falls back to the default host here rather than throwing during cert review.
|
|
454
|
+
const name = urlToNodeName(getThisNodeUrl());
|
|
455
|
+
if (name == null) {
|
|
397
456
|
const host = CERT_DOMAINS[0];
|
|
398
457
|
logger.info?.('node url is missing from harperdb-config.yaml, using default host' + host);
|
|
399
458
|
return host;
|
|
400
459
|
}
|
|
401
|
-
return
|
|
460
|
+
return name;
|
|
402
461
|
}
|
|
403
462
|
|
|
404
463
|
export function getCommonName() {
|
|
@@ -990,10 +1049,79 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
990
1049
|
let subscribedTable = null;
|
|
991
1050
|
let activeSubscription: Promise<any> | null = null;
|
|
992
1051
|
return ((SNICallback as any).ready = new Promise<void>((resolve, reject) => {
|
|
1052
|
+
// Pass-level failure before `.ready` settles still rejects (boot surfaces it); after,
|
|
1053
|
+
// the only safe response is to keep serving live state and retry.
|
|
1054
|
+
let readySettled = false;
|
|
1055
|
+
const settle = (value?) => {
|
|
1056
|
+
readySettled = true;
|
|
1057
|
+
resolve(value);
|
|
1058
|
+
};
|
|
1059
|
+
// Self-retry state for passes with per-record failures: the signature identifies the
|
|
1060
|
+
// failure set, the delay backs off while it is unchanged, and summaries are rate-limited.
|
|
1061
|
+
let failureSignature = '';
|
|
1062
|
+
let failureRetryDelay = TLS_REBUILD_DEBOUNCE_MS;
|
|
1063
|
+
let failureRetryTimer;
|
|
1064
|
+
let failureRetryCount = 0;
|
|
1065
|
+
let failureLastSummaryAt = 0;
|
|
1066
|
+
const scheduleFailureRetry = () => {
|
|
1067
|
+
// An armed external rebuild (or an armed retry) will run a pass that re-arms if needed.
|
|
1068
|
+
if (failureRetryTimer || rebuildTimer) return;
|
|
1069
|
+
failureRetryTimer = setTimeout(() => {
|
|
1070
|
+
failureRetryTimer = undefined;
|
|
1071
|
+
updateTLS();
|
|
1072
|
+
}, failureRetryDelay).unref();
|
|
1073
|
+
failureRetryDelay = Math.min(failureRetryDelay * 2, TLS_FAILURE_RETRY_MAX_DELAY_MS);
|
|
1074
|
+
};
|
|
1075
|
+
// Log each failure on a new/changed failure set; while unchanged, stay silent apart from a
|
|
1076
|
+
// rate-limited summary — an un-throttled 1.5s retry would emit ~57k lines/day per selector.
|
|
1077
|
+
const reportFailures = (failures: { cert: any; error: any }[]) => {
|
|
1078
|
+
const signature = failures
|
|
1079
|
+
.map(({ cert, error }) => `${cert.name}: ${error?.message}`)
|
|
1080
|
+
.sort()
|
|
1081
|
+
.join('; ');
|
|
1082
|
+
if (signature !== failureSignature) {
|
|
1083
|
+
failureSignature = signature;
|
|
1084
|
+
failureRetryDelay = TLS_REBUILD_DEBOUNCE_MS;
|
|
1085
|
+
failureRetryCount = 0;
|
|
1086
|
+
failureLastSummaryAt = Date.now();
|
|
1087
|
+
// A timer armed for the previous signature would run the next pass on its stale
|
|
1088
|
+
// (possibly maxed-out) delay; re-arm fresh for the new failure set.
|
|
1089
|
+
if (failureRetryTimer) {
|
|
1090
|
+
clearTimeout(failureRetryTimer);
|
|
1091
|
+
failureRetryTimer = undefined;
|
|
1092
|
+
}
|
|
1093
|
+
for (const { cert, error } of failures)
|
|
1094
|
+
logger.error?.('Error applying TLS for', cert.name, `on the '${type}' listener`, error);
|
|
1095
|
+
} else {
|
|
1096
|
+
failureRetryCount++;
|
|
1097
|
+
// Stays at error: a stuck rotation must keep an alertable signal — the retained cert
|
|
1098
|
+
// is aging toward its notAfter the whole time.
|
|
1099
|
+
if (Date.now() - failureLastSummaryAt >= TLS_FAILURE_SUMMARY_INTERVAL_MS) {
|
|
1100
|
+
failureLastSummaryAt = Date.now();
|
|
1101
|
+
logger.error?.(
|
|
1102
|
+
`TLS rebuild for the '${type}' listener is still failing for [${failures
|
|
1103
|
+
.map(({ cert }) => cert.name)
|
|
1104
|
+
.join(', ')}] after ${failureRetryCount} retries; serving the last good state`
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
const clearFailureState = () => {
|
|
1110
|
+
if (failureSignature) {
|
|
1111
|
+
logger.warn?.(`TLS rebuild for the '${type}' listener recovered; all certificates applied`);
|
|
1112
|
+
}
|
|
1113
|
+
failureSignature = '';
|
|
1114
|
+
failureRetryDelay = TLS_REBUILD_DEBOUNCE_MS;
|
|
1115
|
+
failureRetryCount = 0;
|
|
1116
|
+
if (failureRetryTimer) {
|
|
1117
|
+
clearTimeout(failureRetryTimer);
|
|
1118
|
+
failureRetryTimer = undefined;
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
993
1121
|
function updateTLS() {
|
|
994
1122
|
try {
|
|
995
1123
|
if (databases === undefined) {
|
|
996
|
-
|
|
1124
|
+
settle();
|
|
997
1125
|
return;
|
|
998
1126
|
}
|
|
999
1127
|
if (databases.system?.hdb_certificate === undefined) {
|
|
@@ -1023,19 +1151,19 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1023
1151
|
scheduleRebuild();
|
|
1024
1152
|
return;
|
|
1025
1153
|
}
|
|
1026
|
-
//
|
|
1027
|
-
//
|
|
1028
|
-
//
|
|
1029
|
-
|
|
1030
|
-
|
|
1154
|
+
// Transactional publication (#2382): build the whole replacement state off to the
|
|
1155
|
+
// side and reconcile the live maps only after the pass completes, so a failure of
|
|
1156
|
+
// any shape leaves the currently-served state intact.
|
|
1157
|
+
const candidateContexts = new Map();
|
|
1158
|
+
const candidateCAs = new Map();
|
|
1159
|
+
let candidateHasWildcards = false;
|
|
1031
1160
|
let bestQuality = 0;
|
|
1032
|
-
|
|
1033
|
-
//
|
|
1034
|
-
//
|
|
1035
|
-
//
|
|
1036
|
-
// forever, which would let a later transient zero-cert rebuild skip the retry and
|
|
1037
|
-
// publish an empty certificates list (the #1998 symptom) on the post-boot path.
|
|
1161
|
+
let candidateDefault;
|
|
1162
|
+
// Whether THIS pass produced a default. The `defaultContext` closure variable is never
|
|
1163
|
+
// reset (a transient zero-cert pass keeps serving the prior default), so keying the
|
|
1164
|
+
// zero-cert retry off it would republish empty post-boot — the #1998 symptom.
|
|
1038
1165
|
let defaultContextSetThisPass = false;
|
|
1166
|
+
const failedThisPass: { cert: any; error: any }[] = [];
|
|
1039
1167
|
// Track the actual table instance, not just whether we've ever subscribed: resetDatabases()
|
|
1040
1168
|
// (copy_db, ITC restart handling) replaces databases.system.hdb_certificate with a new
|
|
1041
1169
|
// table object, and a boolean flag would never re-subscribe to it, permanently losing
|
|
@@ -1065,24 +1193,28 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1065
1193
|
previousSubscription.then((subscription: any) => subscription?.end?.()).catch(() => {});
|
|
1066
1194
|
}
|
|
1067
1195
|
}
|
|
1068
|
-
|
|
1196
|
+
// One snapshot drives both loops: separate search() calls own separate read
|
|
1197
|
+
// snapshots, and a write committing between them could publish a renewed leaf
|
|
1198
|
+
// against the previous CA set.
|
|
1199
|
+
const certRecords = Array.from(databases.system.hdb_certificate.search([])) as any[];
|
|
1200
|
+
for (const cert of certRecords) {
|
|
1069
1201
|
const certificate = cert.certificate;
|
|
1070
1202
|
let certParsed;
|
|
1071
1203
|
try {
|
|
1072
1204
|
certParsed = new X509Certificate(certificate);
|
|
1073
1205
|
} catch (error) {
|
|
1074
|
-
//
|
|
1075
|
-
//
|
|
1076
|
-
|
|
1206
|
+
// A pass failure like any other (throttle + retry). Its caCerts entry can't be
|
|
1207
|
+
// retained — the subject is unrecoverable from a PEM that won't parse.
|
|
1208
|
+
failedThisPass.push({ cert, error });
|
|
1077
1209
|
continue;
|
|
1078
1210
|
}
|
|
1079
1211
|
if (cert.is_authority) {
|
|
1080
1212
|
(certParsed as any).asString = certificate;
|
|
1081
|
-
|
|
1213
|
+
candidateCAs.set(certParsed.subject, certificate);
|
|
1082
1214
|
}
|
|
1083
1215
|
}
|
|
1084
1216
|
|
|
1085
|
-
for (const cert of
|
|
1217
|
+
for (const cert of certRecords) {
|
|
1086
1218
|
try {
|
|
1087
1219
|
if (cert.is_authority) {
|
|
1088
1220
|
continue;
|
|
@@ -1100,8 +1232,8 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1100
1232
|
|
|
1101
1233
|
let certificate = cert.certificate;
|
|
1102
1234
|
const certParsed = new X509Certificate(certificate);
|
|
1103
|
-
if (
|
|
1104
|
-
certificate += '\n' +
|
|
1235
|
+
if (candidateCAs.has(certParsed.issuer)) {
|
|
1236
|
+
certificate += '\n' + candidateCAs.get(certParsed.issuer);
|
|
1105
1237
|
}
|
|
1106
1238
|
if (!private_key || !certificate) {
|
|
1107
1239
|
throw new Error('Missing private key or certificate for secure server');
|
|
@@ -1109,8 +1241,10 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1109
1241
|
const secureOptions = {
|
|
1110
1242
|
ciphers: cert.ciphers,
|
|
1111
1243
|
ticketKeys: getTicketKeys(),
|
|
1112
|
-
|
|
1113
|
-
|
|
1244
|
+
// the live map, not the candidate: its identity survives publication, so contexts
|
|
1245
|
+
// keep seeing the current CA set after later rebuilds
|
|
1246
|
+
availableCAs: caCerts,
|
|
1247
|
+
ca: mtlsOptions && Array.from(candidateCAs.values()),
|
|
1114
1248
|
cert: certificate,
|
|
1115
1249
|
key: private_key,
|
|
1116
1250
|
key_file: cert.private_key_name,
|
|
@@ -1126,7 +1260,7 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1126
1260
|
(secureContext as any).name = cert.name;
|
|
1127
1261
|
(secureContext as any).options = secureOptions;
|
|
1128
1262
|
(secureContext as any).quality = quality;
|
|
1129
|
-
(secureContext as any).certificateAuthorities = Array.from(
|
|
1263
|
+
(secureContext as any).certificateAuthorities = Array.from(candidateCAs);
|
|
1130
1264
|
// we store the first 100 bytes of the certificate just for debug logging
|
|
1131
1265
|
(secureContext as any).certStart = certificate.toString().slice(0, 100);
|
|
1132
1266
|
// we want to configure SNI handling to pick the right certificate based on all the registered SANs
|
|
@@ -1134,14 +1268,14 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1134
1268
|
for (let hostname of hostnames) {
|
|
1135
1269
|
if (hostname) {
|
|
1136
1270
|
if (hostname[0] === '*') {
|
|
1137
|
-
|
|
1271
|
+
candidateHasWildcards = true;
|
|
1138
1272
|
hostname = hostname.slice(1);
|
|
1139
1273
|
}
|
|
1140
1274
|
// we use this certificate if it has a higher quality than the existing one for this hostname
|
|
1141
|
-
let existingCertQuality =
|
|
1275
|
+
let existingCertQuality = candidateContexts.get(hostname)?.quality ?? 0;
|
|
1142
1276
|
logger.trace?.('Assigning TLS for hostname', hostname, 'if', quality, '>', existingCertQuality);
|
|
1143
1277
|
if (quality > existingCertQuality) {
|
|
1144
|
-
|
|
1278
|
+
candidateContexts.set(hostname, secureContext);
|
|
1145
1279
|
}
|
|
1146
1280
|
} else {
|
|
1147
1281
|
logger.error?.('No hostname found for certificate at', (tls as any).certificate);
|
|
@@ -1163,55 +1297,141 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1163
1297
|
);
|
|
1164
1298
|
if (quality > bestQuality /* && hasIpAddress*/) {
|
|
1165
1299
|
// we use this certificate as the default if it has a higher quality than the existing one
|
|
1166
|
-
|
|
1300
|
+
candidateDefault = secureContext;
|
|
1167
1301
|
defaultContextSetThisPass = true;
|
|
1168
1302
|
bestQuality = quality;
|
|
1169
|
-
if (server) {
|
|
1170
|
-
server.defaultContext = secureContext;
|
|
1171
|
-
// note that we can not set the secure context on the server here, because this creates an
|
|
1172
|
-
// indeterminate situation of whether openssl will use this certificate or the one from the SNI
|
|
1173
|
-
// callback
|
|
1174
|
-
//server.setSecureContext?.(server, secureOptions);
|
|
1175
|
-
}
|
|
1176
1303
|
}
|
|
1177
1304
|
} catch (error) {
|
|
1178
|
-
|
|
1305
|
+
failedThisPass.push({ cert, error });
|
|
1179
1306
|
}
|
|
1180
1307
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
//
|
|
1201
|
-
//
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1308
|
+
|
|
1309
|
+
// Retain-last-good: a record still in the table whose build failed keeps its live
|
|
1310
|
+
// hostname entries and its default candidacy; deletion remains the way to drop them.
|
|
1311
|
+
// On an mTLS listener whose CA set changed, the retained pair is rebuilt against the
|
|
1312
|
+
// current trust material — new handshakes never see stale trust (established sessions
|
|
1313
|
+
// and outstanding tickets are unaffected, as on any build); if the rebuild fails the entries drop
|
|
1314
|
+
// unless nothing else is servable (the zero-cert guard below then keeps the old
|
|
1315
|
+
// state). Full contract in DESIGN.md "TLS hot-reload".
|
|
1316
|
+
const caSetUnchanged = (previous) => {
|
|
1317
|
+
const builtWith = (previous as any).certificateAuthorities;
|
|
1318
|
+
if (!Array.isArray(builtWith)) return candidateCAs.size === 0;
|
|
1319
|
+
return (
|
|
1320
|
+
builtWith.length === candidateCAs.size &&
|
|
1321
|
+
builtWith.every(([subject, pem]) => candidateCAs.get(subject) === pem)
|
|
1322
|
+
);
|
|
1323
|
+
};
|
|
1324
|
+
const rebuiltRetentions = new Map();
|
|
1325
|
+
const retentionFailures: { cert: any; error: any }[] = [];
|
|
1326
|
+
const retainable = (previous) => {
|
|
1327
|
+
// Without mTLS nothing consulted depends on the CA set — skip the rebuild, but
|
|
1328
|
+
// refresh the CA bookkeeping mirrored into socket metadata for fronting proxies.
|
|
1329
|
+
if (!mtlsOptions || caSetUnchanged(previous)) {
|
|
1330
|
+
(previous as any).certificateAuthorities = Array.from(candidateCAs);
|
|
1331
|
+
return previous;
|
|
1332
|
+
}
|
|
1333
|
+
if (rebuiltRetentions.has(previous)) return rebuiltRetentions.get(previous);
|
|
1334
|
+
try {
|
|
1335
|
+
const secureOptions = {
|
|
1336
|
+
...(previous as any).options,
|
|
1337
|
+
ticketKeys: getTicketKeys(),
|
|
1338
|
+
availableCAs: caCerts,
|
|
1339
|
+
ca: mtlsOptions && Array.from(candidateCAs.values()),
|
|
1340
|
+
};
|
|
1341
|
+
const rebuilt = tls.createSecureContext(secureOptions);
|
|
1342
|
+
(rebuilt as any).name = (previous as any).name;
|
|
1343
|
+
(rebuilt as any).options = secureOptions;
|
|
1344
|
+
(rebuilt as any).quality = (previous as any).quality;
|
|
1345
|
+
(rebuilt as any).certificateAuthorities = Array.from(candidateCAs);
|
|
1346
|
+
(rebuilt as any).certStart = (previous as any).certStart;
|
|
1347
|
+
rebuiltRetentions.set(previous, rebuilt);
|
|
1348
|
+
return rebuilt;
|
|
1349
|
+
} catch (error) {
|
|
1350
|
+
retentionFailures.push({
|
|
1351
|
+
cert: { name: `${(previous as any).name} (retained-context rebuild)` },
|
|
1352
|
+
error,
|
|
1353
|
+
});
|
|
1354
|
+
rebuiltRetentions.set(previous, undefined);
|
|
1355
|
+
return undefined;
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
for (const { cert } of failedThisPass) {
|
|
1359
|
+
// Ties go to the incumbent: a hostname the retained context already owned, and the
|
|
1360
|
+
// live default when it is the retained record, must not flip to an equal-quality
|
|
1361
|
+
// sibling because of a transient failure. A context retained only for a hostname
|
|
1362
|
+
// still needs strict > to become the default.
|
|
1363
|
+
const retain = (previous, hostname?) => {
|
|
1364
|
+
const retained = retainable(previous);
|
|
1365
|
+
if (!retained) return;
|
|
1366
|
+
const previousQuality = (retained as any).quality ?? 0;
|
|
1367
|
+
if (hostname !== undefined) {
|
|
1368
|
+
if (previousQuality >= ((candidateContexts.get(hostname) as any)?.quality ?? 0)) {
|
|
1369
|
+
candidateContexts.set(hostname, retained);
|
|
1370
|
+
}
|
|
1371
|
+
if (hostname[0] === '.') candidateHasWildcards = true;
|
|
1372
|
+
}
|
|
1373
|
+
const winsDefault =
|
|
1374
|
+
previous === defaultContext ? previousQuality >= bestQuality : previousQuality > bestQuality;
|
|
1375
|
+
if (winsDefault) {
|
|
1376
|
+
candidateDefault = retained;
|
|
1377
|
+
defaultContextSetThisPass = true;
|
|
1378
|
+
bestQuality = previousQuality;
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
for (const [hostname, previous] of secureContexts) {
|
|
1382
|
+
if ((previous as any).name === cert.name) retain(previous, hostname);
|
|
1383
|
+
}
|
|
1384
|
+
if ((defaultContext as any)?.name === cert.name) retain(defaultContext);
|
|
1385
|
+
}
|
|
1386
|
+
if (liveReload && candidateContexts.size === 0 && !defaultContextSetThisPass) {
|
|
1387
|
+
// Every row failed to apply (e.g. keys not yet on this thread). Publishing or
|
|
1388
|
+
// resolving would write an empty `certificates:` list (the #1998 symptom), so keep
|
|
1389
|
+
// live state and retry. `!defaultContextSetThisPass`, not the persistent
|
|
1390
|
+
// `defaultContext`: a cert with no usable hostnames still sets a serviceable
|
|
1391
|
+
// default (must resolve, not retry forever), while the closure variable is truthy
|
|
1392
|
+
// forever after the first success. Gated on liveReload: transient selectors
|
|
1393
|
+
// (getReplicationCert) legitimately resolve empty — bootstrap depends on that
|
|
1394
|
+
// meaning "no cert yet". Warn is latched: unlatched would emit ~57k lines/day.
|
|
1206
1395
|
if (server && !server.tlsSelectorWarnedZeroCerts) {
|
|
1207
1396
|
server.tlsSelectorWarnedZeroCerts = true;
|
|
1208
1397
|
logger.warn?.(
|
|
1209
1398
|
`TLS selector for the '${type}' listener resolved zero certificates; retrying every ${TLS_REBUILD_DEBOUNCE_MS}ms`
|
|
1210
1399
|
);
|
|
1211
1400
|
}
|
|
1212
|
-
|
|
1401
|
+
if (failedThisPass.length > 0) {
|
|
1402
|
+
// Surface WHY the pass came up empty and put the retry on the backoff.
|
|
1403
|
+
reportFailures(failedThisPass.concat(retentionFailures));
|
|
1404
|
+
scheduleFailureRetry();
|
|
1405
|
+
} else {
|
|
1406
|
+
scheduleRebuild();
|
|
1407
|
+
}
|
|
1213
1408
|
return;
|
|
1214
1409
|
}
|
|
1410
|
+
// Publish: reconcile the live maps in place (their identity is aliased by
|
|
1411
|
+
// server.secureContexts and every context's availableCAs) and advance all default
|
|
1412
|
+
// references together so SNI and non-SNI traffic can't diverge.
|
|
1413
|
+
secureContexts.clear();
|
|
1414
|
+
for (const [hostname, context] of candidateContexts) secureContexts.set(hostname, context);
|
|
1415
|
+
caCerts.clear();
|
|
1416
|
+
for (const [subject, certificate] of candidateCAs) caCerts.set(subject, certificate);
|
|
1417
|
+
// only listener selectors publish: a one-shot client selector's pass may see no authority rows
|
|
1418
|
+
if (liveReload) publishTrustedAuthorities(caCerts.values());
|
|
1419
|
+
hasWildcards = candidateHasWildcards;
|
|
1420
|
+
if (candidateDefault) {
|
|
1421
|
+
(SNICallback as any).defaultContext = defaultContext = candidateDefault;
|
|
1422
|
+
// note that we can not set the secure context on the server here, because this creates an
|
|
1423
|
+
// indeterminate situation of whether openssl will use this certificate or the one from the SNI
|
|
1424
|
+
// callback
|
|
1425
|
+
if (server) server.defaultContext = candidateDefault;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
if (failedThisPass.length > 0) {
|
|
1429
|
+
reportFailures(failedThisPass.concat(retentionFailures));
|
|
1430
|
+
scheduleFailureRetry();
|
|
1431
|
+
} else {
|
|
1432
|
+
clearFailureState();
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1215
1435
|
// A successful pass ends any warn latches so a later recurrence logs again.
|
|
1216
1436
|
if (server) {
|
|
1217
1437
|
server.tlsSelectorWaitedForSystemDb = false;
|
|
@@ -1237,8 +1457,14 @@ export function createTLSSelector(type, mtlsOptions?, liveReload = true): any {
|
|
|
1237
1457
|
}
|
|
1238
1458
|
}
|
|
1239
1459
|
server?.secureContextsListeners.forEach((listener) => listener());
|
|
1240
|
-
|
|
1460
|
+
settle(defaultContext);
|
|
1241
1461
|
} catch (error) {
|
|
1462
|
+
if (readySettled) {
|
|
1463
|
+
// Live state is untouched (candidates are pass-local): keep serving it and retry.
|
|
1464
|
+
reportFailures([{ cert: { name: `(${type} rebuild pass)` }, error }]);
|
|
1465
|
+
scheduleFailureRetry();
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1242
1468
|
reject(error);
|
|
1243
1469
|
}
|
|
1244
1470
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A token operation scope (#2174) narrows a credential to a subset of its user's operations.
|
|
3
|
+
*
|
|
4
|
+
* It lives under two property names depending on the carrier: the claim `operations` on a JWT
|
|
5
|
+
* payload, and `tokenOperations` on an in-memory user principal (validateToken lifts the claim onto
|
|
6
|
+
* the user; verifyPerms reads it there). Every path that PRODUCES a credential or principal must
|
|
7
|
+
* carry the scope forward or the result is unscoped — the five bypasses this feature had to close
|
|
8
|
+
* were each a produce-a-credential path that forgot to. So the guard lives here, called at each such
|
|
9
|
+
* site, rather than re-inlined as `Array.isArray(...)` in six places that could drift apart.
|
|
10
|
+
*
|
|
11
|
+
* A present scope is an array — INCLUDING an empty (deny-all) array, which must be preserved rather
|
|
12
|
+
* than treated as "no scope". Anything else (absent/null) is skipped, so unscoped credentials behave
|
|
13
|
+
* exactly as they did before this existed.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type OperationScope = string[];
|
|
17
|
+
|
|
18
|
+
/** True for a present scope: an array, including the empty deny-all array. */
|
|
19
|
+
export function hasOperationScope(scope: unknown): scope is OperationScope {
|
|
20
|
+
return Array.isArray(scope);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Copies a present scope onto a JWT payload (claim name `operations`). Returns the payload. */
|
|
24
|
+
export function attachScopeToToken<T extends object>(payload: T, scope: unknown): T {
|
|
25
|
+
if (hasOperationScope(scope)) (payload as any).operations = scope;
|
|
26
|
+
return payload;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Copies a present scope onto a user principal (property `tokenOperations`). Returns the user. */
|
|
30
|
+
export function attachScopeToUser<T extends object>(user: T, scope: unknown): T {
|
|
31
|
+
if (hasOperationScope(scope)) (user as any).tokenOperations = scope;
|
|
32
|
+
return user;
|
|
33
|
+
}
|
|
@@ -13,6 +13,13 @@ module.exports = {
|
|
|
13
13
|
const rolePermsMap = Object.create(null);
|
|
14
14
|
const permsTemplateObj = (permsKey) => ({ key: permsKey, perms: {} });
|
|
15
15
|
|
|
16
|
+
// Synthetic roles (impersonation/scoped tokens, names prefixed '_' by syntheticRoleName) have
|
|
17
|
+
// content-derived names, so their population is unbounded — they get their own LRU-bounded map
|
|
18
|
+
// instead of the permanent rolePermsMap, or minting many distinct permission sets would grow a
|
|
19
|
+
// per-worker memo that is never freed.
|
|
20
|
+
const MAX_SYNTHETIC_ROLE_ENTRIES = 256;
|
|
21
|
+
const syntheticRolePermsMap = new Map();
|
|
22
|
+
|
|
16
23
|
const schemaPermsTemplate = (describePerm = false) => ({
|
|
17
24
|
describe: describePerm,
|
|
18
25
|
tables: {},
|
|
@@ -81,6 +88,22 @@ function getRolePermissions(role) {
|
|
|
81
88
|
// translation to get an updated permissions set
|
|
82
89
|
const permsKey = JSON.stringify([role['__updatedtime__'], nonSysSchema]);
|
|
83
90
|
|
|
91
|
+
if (roleName.startsWith('_')) {
|
|
92
|
+
const cached = syntheticRolePermsMap.get(roleName);
|
|
93
|
+
if (cached && cached.key === permsKey) {
|
|
94
|
+
syntheticRolePermsMap.delete(roleName);
|
|
95
|
+
syntheticRolePermsMap.set(roleName, cached);
|
|
96
|
+
return cached.perms;
|
|
97
|
+
}
|
|
98
|
+
const newRolePerms = translateRolePermissions(role, nonSysSchema);
|
|
99
|
+
syntheticRolePermsMap.delete(roleName);
|
|
100
|
+
syntheticRolePermsMap.set(roleName, { key: permsKey, perms: newRolePerms });
|
|
101
|
+
if (syntheticRolePermsMap.size > MAX_SYNTHETIC_ROLE_ENTRIES) {
|
|
102
|
+
syntheticRolePermsMap.delete(syntheticRolePermsMap.keys().next().value);
|
|
103
|
+
}
|
|
104
|
+
return newRolePerms;
|
|
105
|
+
}
|
|
106
|
+
|
|
84
107
|
//If key exists already, we can return the cached value
|
|
85
108
|
if (rolePermsMap[roleName] && rolePermsMap[roleName].key === permsKey) {
|
|
86
109
|
return rolePermsMap[roleName].perms;
|