From 7533369c638d86ce59c493272f7e69667d360b95 Mon Sep 17 00:00:00 2001
From: Pascal Bleser
Date: Wed, 10 Jun 2026 10:47:17 +0200
Subject: [PATCH] groupware: port opencloud_full to Stalwart 0.16 (only the
built-in IDM LDAP option for now)
---
.../config/stalwart/config.json | 1 +
.../config/stalwart/idmldap.json | 24 ++++
.../config/stalwart/idmldap.toml | 113 ------------------
.../opencloud_full/stalwart-snapshot | 27 +++++
.../stalwart-snapshot-passwords | 66 ++++++++++
.../deployments/opencloud_full/stalwart.yml | 90 +++++++++++++-
6 files changed, 202 insertions(+), 119 deletions(-)
create mode 100644 devtools/deployments/opencloud_full/config/stalwart/config.json
create mode 100644 devtools/deployments/opencloud_full/config/stalwart/idmldap.json
delete mode 100644 devtools/deployments/opencloud_full/config/stalwart/idmldap.toml
create mode 100755 devtools/deployments/opencloud_full/stalwart-snapshot
create mode 100755 devtools/deployments/opencloud_full/stalwart-snapshot-passwords
diff --git a/devtools/deployments/opencloud_full/config/stalwart/config.json b/devtools/deployments/opencloud_full/config/stalwart/config.json
new file mode 100644
index 0000000000..96e03438b5
--- /dev/null
+++ b/devtools/deployments/opencloud_full/config/stalwart/config.json
@@ -0,0 +1 @@
+{"@type":"RocksDb","path":"/var/lib/stalwart/","blobSize":16834,"bufferSize":134217728,"poolWorkers":null}
diff --git a/devtools/deployments/opencloud_full/config/stalwart/idmldap.json b/devtools/deployments/opencloud_full/config/stalwart/idmldap.json
new file mode 100644
index 0000000000..2a4971a3d1
--- /dev/null
+++ b/devtools/deployments/opencloud_full/config/stalwart/idmldap.json
@@ -0,0 +1,24 @@
+{"@type":"destroy","object":"Tracer"}
+{"@type":"destroy","object":"NetworkListener"}
+{"@type":"destroy","object":"DnsServer"}
+{"@type":"destroy","object":"Certificate"}
+{"@type":"destroy","object":"Tenant"}
+{"@type":"destroy","object":"Role"}
+{"@type":"destroy","object":"AcmeProvider"}
+{"@type":"destroy","object":"Directory"}
+{"@type":"destroy","object":"Domain"}
+{"@type":"destroy","object":"Account"}
+{"@type":"destroy","object":"DkimSignature"}
+{"@type":"create","object":"Tracer","value":{"tracer-ivbjw13yaaaa":{"@type":"Stdout","level":"trace","ansi":false,"lossy":false,"eventsPolicy":"exclude","events":{"store.data-write":true,"store.data-iterate":true,"store.data-store-purged":true,"store.blob-read":true,"store.blob-write":true,"store.blob-delete":true,"store.blob-store-purged":true,"task-manager.task-acquired":true,"task-manager.task-queued":true,"task-manager.scheduler-started":true,"task-manager.manager-started":true,"eval.result":true},"buffered":true,"enable":true,"multiline":false}}}
+{"@type":"create","object":"NetworkListener","value":{"networklistener-ivbjw137adqa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"smtp","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":false,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"smtp","bind":{"[::]:25":true}},"networklistener-ivbjw137adaa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"submissions","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":true,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"smtp","bind":{"[::]:465":true}},"networklistener-ivbjw137acqa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"imaps","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":true,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"imap","bind":{"[::]:993":true}},"networklistener-ivbjw137acaa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"pop3s","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":true,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"pop3","bind":{"[::]:995":true}},"networklistener-ivbjw137abqa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"sieve","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":false,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"manageSieve","bind":{"[::]:4190":true}},"networklistener-ivbjw137abaa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"https","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":true,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"http","bind":{"[::]:443":true}},"networklistener-ivbjw137aaqa":{"socketBacklog":1024,"socketTosV4":null,"tlsTimeout":60000,"name":"http","socketNoDelay":true,"socketSendBufferSize":null,"maxConnections":8192,"socketReuseAddress":true,"tlsIgnoreClientOrder":true,"socketReceiveBufferSize":null,"socketReusePort":true,"overrideProxyTrustedNetworks":{},"useTls":true,"tlsDisableCipherSuites":{},"tlsImplicit":false,"tlsDisableProtocols":{},"socketTtl":null,"protocol":"http","bind":{"[::]:8080":true}}}}
+{"@type":"create","object":"Role","value":{"role-e":{"description":"System Administrator","enabledPermissions":{"authenticate":true,"authenticateWithAlias":true,"interactAi":true,"impersonate":true,"unlimitedRequests":true,"unlimitedUploads":true,"fetchAnyBlob":true,"oAuthClientRegistration":true,"oAuthClientOverride":true,"liveTracing":true,"liveMetrics":true,"liveDeliveryTest":true,"sysAccountGet":true,"sysAccountCreate":true,"sysAccountUpdate":true,"sysAccountDestroy":true,"sysAccountQuery":true,"sysAccountPasswordGet":true,"sysAccountPasswordUpdate":true,"sysAccountSettingsGet":true,"sysAccountSettingsUpdate":true,"sysAcmeProviderGet":true,"sysAcmeProviderCreate":true,"sysAcmeProviderUpdate":true,"sysAcmeProviderDestroy":true,"sysAcmeProviderQuery":true,"actionReloadSettings":true,"actionReloadTlsCertificates":true,"actionReloadLookupStores":true,"actionReloadBlockedIps":true,"actionUpdateApps":true,"actionTroubleshootDmarc":true,"actionClassifySpam":true,"actionInvalidateCaches":true,"actionInvalidateNegativeCaches":true,"actionPauseMtaQueue":true,"actionResumeMtaQueue":true,"sysActionGet":true,"sysActionCreate":true,"sysActionUpdate":true,"sysActionDestroy":true,"sysActionQuery":true,"sysAddressBookGet":true,"sysAddressBookUpdate":true,"sysAiModelGet":true,"sysAiModelCreate":true,"sysAiModelUpdate":true,"sysAiModelDestroy":true,"sysAiModelQuery":true,"sysAlertGet":true,"sysAlertCreate":true,"sysAlertUpdate":true,"sysAlertDestroy":true,"sysAlertQuery":true,"sysAllowedIpGet":true,"sysAllowedIpCreate":true,"sysAllowedIpUpdate":true,"sysAllowedIpDestroy":true,"sysAllowedIpQuery":true,"sysApiKeyGet":true,"sysApiKeyCreate":true,"sysApiKeyUpdate":true,"sysApiKeyDestroy":true,"sysApiKeyQuery":true,"sysAppPasswordGet":true,"sysAppPasswordCreate":true,"sysAppPasswordUpdate":true,"sysAppPasswordDestroy":true,"sysAppPasswordQuery":true,"sysApplicationGet":true,"sysApplicationCreate":true,"sysApplicationUpdate":true,"sysApplicationDestroy":true,"sysApplicationQuery":true,"sysArchivedItemGet":true,"sysArchivedItemCreate":true,"sysArchivedItemUpdate":true,"sysArchivedItemDestroy":true,"sysArchivedItemQuery":true,"sysArfExternalReportGet":true,"sysArfExternalReportCreate":true,"sysArfExternalReportUpdate":true,"sysArfExternalReportDestroy":true,"sysArfExternalReportQuery":true,"sysAsnGet":true,"sysAsnUpdate":true,"sysAuthenticationGet":true,"sysAuthenticationUpdate":true,"sysBlobStoreGet":true,"sysBlobStoreUpdate":true,"sysBlockedIpGet":true,"sysBlockedIpCreate":true,"sysBlockedIpUpdate":true,"sysBlockedIpDestroy":true,"sysBlockedIpQuery":true,"sysBootstrapGet":true,"sysBootstrapUpdate":true,"sysCacheGet":true,"sysCacheUpdate":true,"sysCalendarGet":true,"sysCalendarUpdate":true,"sysCalendarAlarmGet":true,"sysCalendarAlarmUpdate":true,"sysCalendarSchedulingGet":true,"sysCalendarSchedulingUpdate":true,"sysCertificateGet":true,"sysCertificateCreate":true,"sysCertificateUpdate":true,"sysCertificateDestroy":true,"sysCertificateQuery":true,"sysClusterNodeGet":true,"sysClusterNodeCreate":true,"sysClusterNodeUpdate":true,"sysClusterNodeDestroy":true,"sysClusterNodeQuery":true,"sysClusterRoleGet":true,"sysClusterRoleCreate":true,"sysClusterRoleUpdate":true,"sysClusterRoleDestroy":true,"sysClusterRoleQuery":true,"sysCoordinatorGet":true,"sysCoordinatorUpdate":true,"sysDataRetentionGet":true,"sysDataRetentionUpdate":true,"sysDataStoreGet":true,"sysDataStoreUpdate":true,"sysDirectoryGet":true,"sysDirectoryCreate":true,"sysDirectoryUpdate":true,"sysDirectoryDestroy":true,"sysDirectoryQuery":true,"sysDkimReportSettingsGet":true,"sysDkimReportSettingsUpdate":true,"sysDkimSignatureGet":true,"sysDkimSignatureCreate":true,"sysDkimSignatureUpdate":true,"sysDkimSignatureDestroy":true,"sysDkimSignatureQuery":true,"sysDmarcExternalReportGet":true,"sysDmarcExternalReportCreate":true,"sysDmarcExternalReportUpdate":true,"sysDmarcExternalReportDestroy":true,"sysDmarcExternalReportQuery":true,"sysDmarcInternalReportGet":true,"sysDmarcInternalReportCreate":true,"sysDmarcInternalReportUpdate":true,"sysDmarcInternalReportDestroy":true,"sysDmarcInternalReportQuery":true,"sysDmarcReportSettingsGet":true,"sysDmarcReportSettingsUpdate":true,"sysDnsResolverGet":true,"sysDnsResolverUpdate":true,"sysDnsServerGet":true,"sysDnsServerCreate":true,"sysDnsServerUpdate":true,"sysDnsServerDestroy":true,"sysDnsServerQuery":true,"sysDomainGet":true,"sysDomainCreate":true,"sysDomainUpdate":true,"sysDomainDestroy":true,"sysDomainQuery":true,"sysDsnReportSettingsGet":true,"sysDsnReportSettingsUpdate":true,"sysEmailGet":true,"sysEmailUpdate":true,"sysEnterpriseGet":true,"sysEnterpriseUpdate":true,"sysEventTracingLevelGet":true,"sysEventTracingLevelCreate":true,"sysEventTracingLevelUpdate":true,"sysEventTracingLevelDestroy":true,"sysEventTracingLevelQuery":true,"sysFileStorageGet":true,"sysFileStorageUpdate":true,"sysHttpGet":true,"sysHttpUpdate":true,"sysHttpFormGet":true,"sysHttpFormUpdate":true,"sysHttpLookupGet":true,"sysHttpLookupCreate":true,"sysHttpLookupUpdate":true,"sysHttpLookupDestroy":true,"sysHttpLookupQuery":true,"sysImapGet":true,"sysImapUpdate":true,"sysInMemoryStoreGet":true,"sysInMemoryStoreUpdate":true,"sysJmapGet":true,"sysJmapUpdate":true,"sysLogGet":true,"sysLogCreate":true,"sysLogUpdate":true,"sysLogDestroy":true,"sysLogQuery":true,"sysMailingListGet":true,"sysMailingListCreate":true,"sysMailingListUpdate":true,"sysMailingListDestroy":true,"sysMailingListQuery":true,"sysMaskedEmailGet":true,"sysMaskedEmailCreate":true,"sysMaskedEmailUpdate":true,"sysMaskedEmailDestroy":true,"sysMaskedEmailQuery":true,"sysMemoryLookupKeyGet":true,"sysMemoryLookupKeyCreate":true,"sysMemoryLookupKeyUpdate":true,"sysMemoryLookupKeyDestroy":true,"sysMemoryLookupKeyQuery":true,"sysMemoryLookupKeyValueGet":true,"sysMemoryLookupKeyValueCreate":true,"sysMemoryLookupKeyValueUpdate":true,"sysMemoryLookupKeyValueDestroy":true,"sysMemoryLookupKeyValueQuery":true,"sysMetricGet":true,"sysMetricCreate":true,"sysMetricUpdate":true,"sysMetricDestroy":true,"sysMetricQuery":true,"sysMetricsGet":true,"sysMetricsUpdate":true,"sysMetricsStoreGet":true,"sysMetricsStoreUpdate":true,"sysMtaConnectionStrategyGet":true,"sysMtaConnectionStrategyCreate":true,"sysMtaConnectionStrategyUpdate":true,"sysMtaConnectionStrategyDestroy":true,"sysMtaConnectionStrategyQuery":true,"sysMtaDeliveryScheduleGet":true,"sysMtaDeliveryScheduleCreate":true,"sysMtaDeliveryScheduleUpdate":true,"sysMtaDeliveryScheduleDestroy":true,"sysMtaDeliveryScheduleQuery":true,"sysMtaExtensionsGet":true,"sysMtaExtensionsUpdate":true,"sysMtaHookGet":true,"sysMtaHookCreate":true,"sysMtaHookUpdate":true,"sysMtaHookDestroy":true,"sysMtaHookQuery":true,"sysMtaInboundSessionGet":true,"sysMtaInboundSessionUpdate":true,"sysMtaInboundThrottleGet":true,"sysMtaInboundThrottleCreate":true,"sysMtaInboundThrottleUpdate":true,"sysMtaInboundThrottleDestroy":true,"sysMtaInboundThrottleQuery":true,"sysMtaMilterGet":true,"sysMtaMilterCreate":true,"sysMtaMilterUpdate":true,"sysMtaMilterDestroy":true,"sysMtaMilterQuery":true,"sysMtaOutboundStrategyGet":true,"sysMtaOutboundStrategyUpdate":true,"sysMtaOutboundThrottleGet":true,"sysMtaOutboundThrottleCreate":true,"sysMtaOutboundThrottleUpdate":true,"sysMtaOutboundThrottleDestroy":true,"sysMtaOutboundThrottleQuery":true,"sysMtaQueueQuotaGet":true,"sysMtaQueueQuotaCreate":true,"sysMtaQueueQuotaUpdate":true,"sysMtaQueueQuotaDestroy":true,"sysMtaQueueQuotaQuery":true,"sysMtaRouteGet":true,"sysMtaRouteCreate":true,"sysMtaRouteUpdate":true,"sysMtaRouteDestroy":true,"sysMtaRouteQuery":true,"sysMtaStageAuthGet":true,"sysMtaStageAuthUpdate":true,"sysMtaStageConnectGet":true,"sysMtaStageConnectUpdate":true,"sysMtaStageDataGet":true,"sysMtaStageDataUpdate":true,"sysMtaStageEhloGet":true,"sysMtaStageEhloUpdate":true,"sysMtaStageMailGet":true,"sysMtaStageMailUpdate":true,"sysMtaStageRcptGet":true,"sysMtaStageRcptUpdate":true,"sysMtaStsGet":true,"sysMtaStsUpdate":true,"sysMtaTlsStrategyGet":true,"sysMtaTlsStrategyCreate":true,"sysMtaTlsStrategyUpdate":true,"sysMtaTlsStrategyDestroy":true,"sysMtaTlsStrategyQuery":true,"sysMtaVirtualQueueGet":true,"sysMtaVirtualQueueCreate":true,"sysMtaVirtualQueueUpdate":true,"sysMtaVirtualQueueDestroy":true,"sysMtaVirtualQueueQuery":true,"sysNetworkListenerGet":true,"sysNetworkListenerCreate":true,"sysNetworkListenerUpdate":true,"sysNetworkListenerDestroy":true,"sysNetworkListenerQuery":true,"sysOAuthClientGet":true,"sysOAuthClientCreate":true,"sysOAuthClientUpdate":true,"sysOAuthClientDestroy":true,"sysOAuthClientQuery":true,"sysOidcProviderGet":true,"sysOidcProviderUpdate":true,"sysPublicKeyGet":true,"sysPublicKeyCreate":true,"sysPublicKeyUpdate":true,"sysPublicKeyDestroy":true,"sysPublicKeyQuery":true,"sysQueuedMessageGet":true,"sysQueuedMessageCreate":true,"sysQueuedMessageUpdate":true,"sysQueuedMessageDestroy":true,"sysQueuedMessageQuery":true,"sysReportSettingsGet":true,"sysReportSettingsUpdate":true,"sysRoleGet":true,"sysRoleCreate":true,"sysRoleUpdate":true,"sysRoleDestroy":true,"sysRoleQuery":true,"sysSearchGet":true,"sysSearchUpdate":true,"sysSearchStoreGet":true,"sysSearchStoreUpdate":true,"sysSecurityGet":true,"sysSecurityUpdate":true,"sysSenderAuthGet":true,"sysSenderAuthUpdate":true,"sysSharingGet":true,"sysSharingUpdate":true,"sysSieveSystemInterpreterGet":true,"sysSieveSystemInterpreterUpdate":true,"sysSieveSystemScriptGet":true,"sysSieveSystemScriptCreate":true,"sysSieveSystemScriptUpdate":true,"sysSieveSystemScriptDestroy":true,"sysSieveSystemScriptQuery":true,"sysSieveUserInterpreterGet":true,"sysSieveUserInterpreterUpdate":true,"sysSieveUserScriptGet":true,"sysSieveUserScriptCreate":true,"sysSieveUserScriptUpdate":true,"sysSieveUserScriptDestroy":true,"sysSieveUserScriptQuery":true,"sysSpamClassifierGet":true,"sysSpamClassifierUpdate":true,"sysSpamDnsblServerGet":true,"sysSpamDnsblServerCreate":true,"sysSpamDnsblServerUpdate":true,"sysSpamDnsblServerDestroy":true,"sysSpamDnsblServerQuery":true,"sysSpamDnsblSettingsGet":true,"sysSpamDnsblSettingsUpdate":true,"sysSpamFileExtensionGet":true,"sysSpamFileExtensionCreate":true,"sysSpamFileExtensionUpdate":true,"sysSpamFileExtensionDestroy":true,"sysSpamFileExtensionQuery":true,"sysSpamLlmGet":true,"sysSpamLlmUpdate":true,"sysSpamPyzorGet":true,"sysSpamPyzorUpdate":true,"sysSpamRuleGet":true,"sysSpamRuleCreate":true,"sysSpamRuleUpdate":true,"sysSpamRuleDestroy":true,"sysSpamRuleQuery":true,"sysSpamSettingsGet":true,"sysSpamSettingsUpdate":true,"sysSpamTagGet":true,"sysSpamTagCreate":true,"sysSpamTagUpdate":true,"sysSpamTagDestroy":true,"sysSpamTagQuery":true,"sysSpamTrainingSampleGet":true,"sysSpamTrainingSampleCreate":true,"sysSpamTrainingSampleUpdate":true,"sysSpamTrainingSampleDestroy":true,"sysSpamTrainingSampleQuery":true,"sysSpfReportSettingsGet":true,"sysSpfReportSettingsUpdate":true,"sysStoreLookupGet":true,"sysStoreLookupCreate":true,"sysStoreLookupUpdate":true,"sysStoreLookupDestroy":true,"sysStoreLookupQuery":true,"sysSystemSettingsGet":true,"sysSystemSettingsUpdate":true,"taskIndexDocument":true,"taskUnindexDocument":true,"taskIndexTrace":true,"taskCalendarAlarmEmail":true,"taskCalendarAlarmNotification":true,"taskCalendarItipMessage":true,"taskMergeThreads":true,"taskDmarcReport":true,"taskTlsReport":true,"taskRestoreArchivedItem":true,"taskDestroyAccount":true,"taskAccountMaintenance":true,"taskTenantMaintenance":true,"taskStoreMaintenance":true,"taskSpamFilterMaintenance":true,"taskAcmeRenewal":true,"taskDkimManagement":true,"taskDnsManagement":true,"sysTaskGet":true,"sysTaskCreate":true,"sysTaskUpdate":true,"sysTaskDestroy":true,"sysTaskQuery":true,"sysTaskManagerGet":true,"sysTaskManagerUpdate":true,"sysTenantGet":true,"sysTenantCreate":true,"sysTenantUpdate":true,"sysTenantDestroy":true,"sysTenantQuery":true,"sysTlsExternalReportGet":true,"sysTlsExternalReportCreate":true,"sysTlsExternalReportUpdate":true,"sysTlsExternalReportDestroy":true,"sysTlsExternalReportQuery":true,"sysTlsInternalReportGet":true,"sysTlsInternalReportCreate":true,"sysTlsInternalReportUpdate":true,"sysTlsInternalReportDestroy":true,"sysTlsInternalReportQuery":true,"sysTlsReportSettingsGet":true,"sysTlsReportSettingsUpdate":true,"sysTraceGet":true,"sysTraceCreate":true,"sysTraceUpdate":true,"sysTraceDestroy":true,"sysTraceQuery":true,"sysTracerGet":true,"sysTracerCreate":true,"sysTracerUpdate":true,"sysTracerDestroy":true,"sysTracerQuery":true,"sysTracingStoreGet":true,"sysTracingStoreUpdate":true,"sysWebDavGet":true,"sysWebDavUpdate":true,"sysWebHookGet":true,"sysWebHookCreate":true,"sysWebHookUpdate":true,"sysWebHookDestroy":true,"sysWebHookQuery":true},"roleIds":{},"memberTenantId":null,"disabledPermissions":{}},"role-d":{"description":"Tenant Administrator","enabledPermissions":{"authenticate":true,"authenticateWithAlias":true,"interactAi":true,"fetchAnyBlob":true,"liveDeliveryTest":true,"sysAccountGet":true,"sysAccountCreate":true,"sysAccountUpdate":true,"sysAccountDestroy":true,"sysAccountQuery":true,"sysAcmeProviderGet":true,"sysAcmeProviderCreate":true,"sysAcmeProviderUpdate":true,"sysAcmeProviderDestroy":true,"sysAcmeProviderQuery":true,"sysDkimSignatureGet":true,"sysDkimSignatureCreate":true,"sysDkimSignatureUpdate":true,"sysDkimSignatureDestroy":true,"sysDkimSignatureQuery":true,"sysDnsServerGet":true,"sysDnsServerCreate":true,"sysDnsServerUpdate":true,"sysDnsServerDestroy":true,"sysDnsServerQuery":true,"sysDomainGet":true,"sysDomainCreate":true,"sysDomainUpdate":true,"sysDomainDestroy":true,"sysDomainQuery":true,"sysMailingListGet":true,"sysMailingListCreate":true,"sysMailingListUpdate":true,"sysMailingListDestroy":true,"sysMailingListQuery":true,"sysOAuthClientGet":true,"sysOAuthClientCreate":true,"sysOAuthClientUpdate":true,"sysOAuthClientDestroy":true,"sysOAuthClientQuery":true,"sysQueuedMessageGet":true,"sysQueuedMessageCreate":true,"sysQueuedMessageUpdate":true,"sysQueuedMessageDestroy":true,"sysQueuedMessageQuery":true,"sysRoleGet":true,"sysRoleCreate":true,"sysRoleUpdate":true,"sysRoleDestroy":true,"sysRoleQuery":true},"roleIds":{},"memberTenantId":null,"disabledPermissions":{}},"role-c":{"description":"Group","enabledPermissions":{"emailSend":true,"emailReceive":true,"calendarAlarmsSend":true,"calendarSchedulingSend":true,"calendarSchedulingReceive":true,"jmapPushSubscriptionGet":true,"jmapPushSubscriptionCreate":true,"jmapPushSubscriptionUpdate":true,"jmapPushSubscriptionDestroy":true,"jmapMailboxGet":true,"jmapMailboxChanges":true,"jmapMailboxQuery":true,"jmapMailboxQueryChanges":true,"jmapMailboxCreate":true,"jmapMailboxUpdate":true,"jmapMailboxDestroy":true,"jmapThreadGet":true,"jmapThreadChanges":true,"jmapEmailGet":true,"jmapEmailChanges":true,"jmapEmailQuery":true,"jmapEmailQueryChanges":true,"jmapEmailCreate":true,"jmapEmailUpdate":true,"jmapEmailDestroy":true,"jmapEmailCopy":true,"jmapEmailImport":true,"jmapEmailParse":true,"jmapSearchSnippetGet":true,"jmapIdentityGet":true,"jmapIdentityChanges":true,"jmapIdentityCreate":true,"jmapIdentityUpdate":true,"jmapIdentityDestroy":true,"jmapEmailSubmissionGet":true,"jmapEmailSubmissionChanges":true,"jmapEmailSubmissionQuery":true,"jmapEmailSubmissionQueryChanges":true,"jmapEmailSubmissionCreate":true,"jmapEmailSubmissionUpdate":true,"jmapEmailSubmissionDestroy":true,"jmapVacationResponseGet":true,"jmapVacationResponseCreate":true,"jmapVacationResponseUpdate":true,"jmapVacationResponseDestroy":true,"jmapSieveScriptGet":true,"jmapSieveScriptQuery":true,"jmapSieveScriptValidate":true,"jmapSieveScriptCreate":true,"jmapSieveScriptUpdate":true,"jmapSieveScriptDestroy":true,"jmapPrincipalGet":true,"jmapPrincipalQuery":true,"jmapPrincipalChanges":true,"jmapPrincipalQueryChanges":true,"jmapPrincipalGetAvailability":true,"jmapPrincipalCreate":true,"jmapPrincipalUpdate":true,"jmapPrincipalDestroy":true,"jmapQuotaGet":true,"jmapQuotaChanges":true,"jmapQuotaQuery":true,"jmapQuotaQueryChanges":true,"jmapBlobGet":true,"jmapBlobCopy":true,"jmapBlobLookup":true,"jmapBlobUpload":true,"jmapAddressBookGet":true,"jmapAddressBookChanges":true,"jmapAddressBookCreate":true,"jmapAddressBookUpdate":true,"jmapAddressBookDestroy":true,"jmapContactCardGet":true,"jmapContactCardChanges":true,"jmapContactCardQuery":true,"jmapContactCardQueryChanges":true,"jmapContactCardCreate":true,"jmapContactCardUpdate":true,"jmapContactCardDestroy":true,"jmapContactCardCopy":true,"jmapContactCardParse":true,"jmapFileNodeGet":true,"jmapFileNodeChanges":true,"jmapFileNodeQuery":true,"jmapFileNodeQueryChanges":true,"jmapFileNodeCreate":true,"jmapFileNodeUpdate":true,"jmapFileNodeDestroy":true,"jmapShareNotificationGet":true,"jmapShareNotificationChanges":true,"jmapShareNotificationQuery":true,"jmapShareNotificationQueryChanges":true,"jmapShareNotificationCreate":true,"jmapShareNotificationUpdate":true,"jmapShareNotificationDestroy":true,"jmapCalendarGet":true,"jmapCalendarChanges":true,"jmapCalendarCreate":true,"jmapCalendarUpdate":true,"jmapCalendarDestroy":true,"jmapCalendarEventGet":true,"jmapCalendarEventChanges":true,"jmapCalendarEventQuery":true,"jmapCalendarEventQueryChanges":true,"jmapCalendarEventCreate":true,"jmapCalendarEventUpdate":true,"jmapCalendarEventDestroy":true,"jmapCalendarEventCopy":true,"jmapCalendarEventParse":true,"jmapCalendarEventNotificationGet":true,"jmapCalendarEventNotificationChanges":true,"jmapCalendarEventNotificationQuery":true,"jmapCalendarEventNotificationQueryChanges":true,"jmapCalendarEventNotificationCreate":true,"jmapCalendarEventNotificationUpdate":true,"jmapCalendarEventNotificationDestroy":true,"jmapParticipantIdentityGet":true,"jmapParticipantIdentityChanges":true,"jmapParticipantIdentityCreate":true,"jmapParticipantIdentityUpdate":true,"jmapParticipantIdentityDestroy":true,"jmapCoreEcho":true,"imapAuthenticate":true,"imapAclGet":true,"imapAclSet":true,"imapMyRights":true,"imapListRights":true,"imapAppend":true,"imapCapability":true,"imapId":true,"imapCopy":true,"imapMove":true,"imapCreate":true,"imapDelete":true,"imapEnable":true,"imapExpunge":true,"imapFetch":true,"imapIdle":true,"imapList":true,"imapLsub":true,"imapNamespace":true,"imapRename":true,"imapSearch":true,"imapSort":true,"imapSelect":true,"imapExamine":true,"imapStatus":true,"imapStore":true,"imapSubscribe":true,"imapThread":true,"pop3Authenticate":true,"pop3List":true,"pop3Uidl":true,"pop3Stat":true,"pop3Retr":true,"pop3Dele":true,"sieveAuthenticate":true,"sieveListScripts":true,"sieveSetActive":true,"sieveGetScript":true,"sievePutScript":true,"sieveDeleteScript":true,"sieveRenameScript":true,"sieveCheckScript":true,"sieveHaveSpace":true,"davSyncCollection":true,"davExpandProperty":true,"davPrincipalAcl":true,"davPrincipalList":true,"davPrincipalMatch":true,"davPrincipalSearch":true,"davPrincipalSearchPropSet":true,"davFilePropFind":true,"davFilePropPatch":true,"davFileGet":true,"davFileMkCol":true,"davFileDelete":true,"davFilePut":true,"davFileCopy":true,"davFileMove":true,"davFileLock":true,"davFileAcl":true,"davCardPropFind":true,"davCardPropPatch":true,"davCardGet":true,"davCardMkCol":true,"davCardDelete":true,"davCardPut":true,"davCardCopy":true,"davCardMove":true,"davCardLock":true,"davCardAcl":true,"davCardQuery":true,"davCardMultiGet":true,"davCalPropFind":true,"davCalPropPatch":true,"davCalGet":true,"davCalMkCol":true,"davCalDelete":true,"davCalPut":true,"davCalCopy":true,"davCalMove":true,"davCalLock":true,"davCalAcl":true,"davCalQuery":true,"davCalMultiGet":true,"davCalFreeBusyQuery":true,"sysAccountSettingsGet":true,"sysAccountSettingsUpdate":true,"sysArchivedItemGet":true,"sysArchivedItemCreate":true,"sysArchivedItemUpdate":true,"sysArchivedItemDestroy":true,"sysArchivedItemQuery":true,"sysMaskedEmailGet":true,"sysMaskedEmailCreate":true,"sysMaskedEmailUpdate":true,"sysMaskedEmailDestroy":true,"sysMaskedEmailQuery":true,"sysPublicKeyGet":true,"sysPublicKeyCreate":true,"sysPublicKeyUpdate":true,"sysPublicKeyDestroy":true,"sysPublicKeyQuery":true,"sysSpamTrainingSampleGet":true,"sysSpamTrainingSampleUpdate":true,"sysSpamTrainingSampleDestroy":true,"sysSpamTrainingSampleQuery":true,"jmapFileNodeCopy":true},"roleIds":{},"memberTenantId":null,"disabledPermissions":{}},"role-b":{"description":"User","enabledPermissions":{"authenticate":true,"authenticateWithAlias":true,"interactAi":true,"emailSend":true,"emailReceive":true,"calendarAlarmsSend":true,"calendarSchedulingSend":true,"calendarSchedulingReceive":true,"jmapPushSubscriptionGet":true,"jmapPushSubscriptionCreate":true,"jmapPushSubscriptionUpdate":true,"jmapPushSubscriptionDestroy":true,"jmapMailboxGet":true,"jmapMailboxChanges":true,"jmapMailboxQuery":true,"jmapMailboxQueryChanges":true,"jmapMailboxCreate":true,"jmapMailboxUpdate":true,"jmapMailboxDestroy":true,"jmapThreadGet":true,"jmapThreadChanges":true,"jmapEmailGet":true,"jmapEmailChanges":true,"jmapEmailQuery":true,"jmapEmailQueryChanges":true,"jmapEmailCreate":true,"jmapEmailUpdate":true,"jmapEmailDestroy":true,"jmapEmailCopy":true,"jmapEmailImport":true,"jmapEmailParse":true,"jmapSearchSnippetGet":true,"jmapIdentityGet":true,"jmapIdentityChanges":true,"jmapIdentityCreate":true,"jmapIdentityUpdate":true,"jmapIdentityDestroy":true,"jmapEmailSubmissionGet":true,"jmapEmailSubmissionChanges":true,"jmapEmailSubmissionQuery":true,"jmapEmailSubmissionQueryChanges":true,"jmapEmailSubmissionCreate":true,"jmapEmailSubmissionUpdate":true,"jmapEmailSubmissionDestroy":true,"jmapVacationResponseGet":true,"jmapVacationResponseCreate":true,"jmapVacationResponseUpdate":true,"jmapVacationResponseDestroy":true,"jmapSieveScriptGet":true,"jmapSieveScriptQuery":true,"jmapSieveScriptValidate":true,"jmapSieveScriptCreate":true,"jmapSieveScriptUpdate":true,"jmapSieveScriptDestroy":true,"jmapPrincipalGet":true,"jmapPrincipalQuery":true,"jmapPrincipalChanges":true,"jmapPrincipalQueryChanges":true,"jmapPrincipalGetAvailability":true,"jmapPrincipalCreate":true,"jmapPrincipalUpdate":true,"jmapPrincipalDestroy":true,"jmapQuotaGet":true,"jmapQuotaChanges":true,"jmapQuotaQuery":true,"jmapQuotaQueryChanges":true,"jmapBlobGet":true,"jmapBlobCopy":true,"jmapBlobLookup":true,"jmapBlobUpload":true,"jmapAddressBookGet":true,"jmapAddressBookChanges":true,"jmapAddressBookCreate":true,"jmapAddressBookUpdate":true,"jmapAddressBookDestroy":true,"jmapContactCardGet":true,"jmapContactCardChanges":true,"jmapContactCardQuery":true,"jmapContactCardQueryChanges":true,"jmapContactCardCreate":true,"jmapContactCardUpdate":true,"jmapContactCardDestroy":true,"jmapContactCardCopy":true,"jmapContactCardParse":true,"jmapFileNodeGet":true,"jmapFileNodeChanges":true,"jmapFileNodeQuery":true,"jmapFileNodeQueryChanges":true,"jmapFileNodeCreate":true,"jmapFileNodeUpdate":true,"jmapFileNodeDestroy":true,"jmapShareNotificationGet":true,"jmapShareNotificationChanges":true,"jmapShareNotificationQuery":true,"jmapShareNotificationQueryChanges":true,"jmapShareNotificationCreate":true,"jmapShareNotificationUpdate":true,"jmapShareNotificationDestroy":true,"jmapCalendarGet":true,"jmapCalendarChanges":true,"jmapCalendarCreate":true,"jmapCalendarUpdate":true,"jmapCalendarDestroy":true,"jmapCalendarEventGet":true,"jmapCalendarEventChanges":true,"jmapCalendarEventQuery":true,"jmapCalendarEventQueryChanges":true,"jmapCalendarEventCreate":true,"jmapCalendarEventUpdate":true,"jmapCalendarEventDestroy":true,"jmapCalendarEventCopy":true,"jmapCalendarEventParse":true,"jmapCalendarEventNotificationGet":true,"jmapCalendarEventNotificationChanges":true,"jmapCalendarEventNotificationQuery":true,"jmapCalendarEventNotificationQueryChanges":true,"jmapCalendarEventNotificationCreate":true,"jmapCalendarEventNotificationUpdate":true,"jmapCalendarEventNotificationDestroy":true,"jmapParticipantIdentityGet":true,"jmapParticipantIdentityChanges":true,"jmapParticipantIdentityCreate":true,"jmapParticipantIdentityUpdate":true,"jmapParticipantIdentityDestroy":true,"jmapCoreEcho":true,"imapAuthenticate":true,"imapAclGet":true,"imapAclSet":true,"imapMyRights":true,"imapListRights":true,"imapAppend":true,"imapCapability":true,"imapId":true,"imapCopy":true,"imapMove":true,"imapCreate":true,"imapDelete":true,"imapEnable":true,"imapExpunge":true,"imapFetch":true,"imapIdle":true,"imapList":true,"imapLsub":true,"imapNamespace":true,"imapRename":true,"imapSearch":true,"imapSort":true,"imapSelect":true,"imapExamine":true,"imapStatus":true,"imapStore":true,"imapSubscribe":true,"imapThread":true,"pop3Authenticate":true,"pop3List":true,"pop3Uidl":true,"pop3Stat":true,"pop3Retr":true,"pop3Dele":true,"sieveAuthenticate":true,"sieveListScripts":true,"sieveSetActive":true,"sieveGetScript":true,"sievePutScript":true,"sieveDeleteScript":true,"sieveRenameScript":true,"sieveCheckScript":true,"sieveHaveSpace":true,"davSyncCollection":true,"davExpandProperty":true,"davPrincipalAcl":true,"davPrincipalList":true,"davPrincipalMatch":true,"davPrincipalSearch":true,"davPrincipalSearchPropSet":true,"davFilePropFind":true,"davFilePropPatch":true,"davFileGet":true,"davFileMkCol":true,"davFileDelete":true,"davFilePut":true,"davFileCopy":true,"davFileMove":true,"davFileLock":true,"davFileAcl":true,"davCardPropFind":true,"davCardPropPatch":true,"davCardGet":true,"davCardMkCol":true,"davCardDelete":true,"davCardPut":true,"davCardCopy":true,"davCardMove":true,"davCardLock":true,"davCardAcl":true,"davCardQuery":true,"davCardMultiGet":true,"davCalPropFind":true,"davCalPropPatch":true,"davCalGet":true,"davCalMkCol":true,"davCalDelete":true,"davCalPut":true,"davCalCopy":true,"davCalMove":true,"davCalLock":true,"davCalAcl":true,"davCalQuery":true,"davCalMultiGet":true,"davCalFreeBusyQuery":true,"sysAccountPasswordGet":true,"sysAccountPasswordUpdate":true,"sysAccountSettingsGet":true,"sysAccountSettingsUpdate":true,"sysApiKeyGet":true,"sysApiKeyCreate":true,"sysApiKeyUpdate":true,"sysApiKeyDestroy":true,"sysApiKeyQuery":true,"sysAppPasswordGet":true,"sysAppPasswordCreate":true,"sysAppPasswordUpdate":true,"sysAppPasswordDestroy":true,"sysAppPasswordQuery":true,"sysArchivedItemGet":true,"sysArchivedItemCreate":true,"sysArchivedItemUpdate":true,"sysArchivedItemDestroy":true,"sysArchivedItemQuery":true,"sysMaskedEmailGet":true,"sysMaskedEmailCreate":true,"sysMaskedEmailUpdate":true,"sysMaskedEmailDestroy":true,"sysMaskedEmailQuery":true,"sysPublicKeyGet":true,"sysPublicKeyCreate":true,"sysPublicKeyUpdate":true,"sysPublicKeyDestroy":true,"sysPublicKeyQuery":true,"sysSpamTrainingSampleGet":true,"sysSpamTrainingSampleUpdate":true,"sysSpamTrainingSampleDestroy":true,"sysSpamTrainingSampleQuery":true,"jmapFileNodeCopy":true},"roleIds":{},"memberTenantId":null,"disabledPermissions":{}}}}
+{"@type":"create","object":"Directory","value":{"directory-ivbjw132aeaa":{"@type":"Ldap","allowInvalidCerts":true,"attrEmailAlias":{},"bindSecret":{"@type":"Value","secret":"admin"},"attrSecret":{"userPassword":true},"attrMemberOf":{"memberOf":true},"filterLogin":"(&(objectClass=person)(mail=?))","filterMemberOf":"(&(objectClass=groupOfNames)(member=?))","poolTimeoutWait":30000,"filterMailbox":"(|(&(objectClass=person)(|(uid=?)(cn=?)(mail=?)(mailAlias=?)))(&(objectClass=groupOfNames)(|(uid=?)(cn=?)(mail=?)(mailAlias=?))))","bindAuthentication":true,"groupClass":"groupOfNames","baseDn":"o=libregraph-idm","attrClass":{"objectClass":true},"attrEmail":{"mail":true},"attrDescription":{"description":true},"poolMaxConnections":10,"memberTenantId":null,"timeout":5000,"useTls":true,"attrSecretChanged":{},"description":"OpenCloud IDM LDAP","url":"ldaps://opencloud:9235","poolTimeoutCreate":30000,"bindDn":"uid=reva,ou=sysusers,o=libregraph-idm","poolTimeoutRecycle":30000}}}
+{"@type":"create","object":"Domain","value":{"domain-b":{"memberTenantId":null,"allowRelaying":false,"subAddressing":{"@type":"Enabled"},"dnsManagement":{"@type":"Manual"},"certificateManagement":{"@type":"Manual"},"catchAllAddress":null,"reportAddressUri":"mailto:postmaster","description":null,"isEnabled":true,"logo":null,"dkimManagement":{"@type":"Manual"},"name":"example.org","aliases":{},"directoryId":null}}}
+{"@type":"create","object":"Account","value":{"account-b":{"@type":"User","quotas":{},"locale":"en_US","memberTenantId":null,"domainId":"#domain-b","roles":{"@type":"User"},"aliases":{},"timeZone":null,"permissions":{"@type":"Merge","enabledPermissions":{"impersonate":true},"disabledPermissions":{}},"encryptionAtRest":{"@type":"Disabled"},"name":"master","memberGroupIds":{},"description":null,"credentials":{"0":{"@type":"Password","secret":"supersecret1234","allowedIps":{},"expiresAt":null}}}}}
+{"@type":"update","object":"Authentication","value":{"defaultUserRoleIds":{},"defaultGroupRoleIds":{},"passwordMinLength":1,"maxApiKeys":5,"passwordMinStrength":"zero","passwordDefaultExpiry":null,"maxAppPasswords":5,"passwordMaxLength":128,"defaultTenantRoleIds":{},"defaultAdminRoleIds":{},"directoryId":"#directory-ivbjw132aeaa","passwordHashAlgorithm":"argon2id"}}
+{"@type":"update","object":"Sharing","value":{"allowDirectoryQueries":false,"maxShares":10}}
+{"@type":"update","object":"SystemSettings","value":{"defaultDomainId":"#domain-b","mailExchangers":{"0":{"priority":10,"hostname":null}},"threadPoolSize":null,"defaultCertificateId":null,"providerInfo":{},"proxyTrustedNetworks":{},"services":{"caldav":{"cleartext":false,"hostname":null},"carddav":{"cleartext":false,"hostname":null},"imap":{"cleartext":false,"hostname":null},"jmap":{"cleartext":false,"hostname":null},"managesieve":{"cleartext":false,"hostname":null},"pop3":{"cleartext":false,"hostname":null},"smtp":{"cleartext":false,"hostname":null},"webdav":{"cleartext":false,"hostname":null}},"defaultHostname":"stalwart.opencloud.test","maxConnections":8192}}
+{"@type":"update","object":"DataRetention","value":{"expungeShareNotifyAfter":2592000000,"holdMtaReportsFor":2592000000,"metricsCollectionInterval":{"@type":"Hourly","minute":0},"expungeSchedule":{"@type":"Daily","hour":0,"minute":0},"maxChangesHistory":10000,"holdTracesFor":2592000000,"archiveDeletedAccountsFor":null,"archiveDeletedItemsFor":null,"blobCleanupSchedule":{"@type":"Daily","hour":4,"minute":0},"dataCleanupSchedule":{"@type":"Daily","hour":2,"minute":0},"expungeTrashAfter":2592000000,"expungeSubmissionsAfter":259200000,"holdMetricsFor":7776000000,"expungeSchedulingInboxAfter":2592000000}}
+{"@type":"update","object":"BlobStore","value":{"@type":"Default"}}
+{"@type":"update","object":"InMemoryStore","value":{"@type":"Default"}}
+{"@type":"update","object":"SearchStore","value":{"@type":"Default"}}
diff --git a/devtools/deployments/opencloud_full/config/stalwart/idmldap.toml b/devtools/deployments/opencloud_full/config/stalwart/idmldap.toml
deleted file mode 100644
index 89e038ac45..0000000000
--- a/devtools/deployments/opencloud_full/config/stalwart/idmldap.toml
+++ /dev/null
@@ -1,113 +0,0 @@
-authentication.fallback-admin.secret = "$6$4qPYDVhaUHkKcY7s$bB6qhcukb9oFNYRIvaDZgbwxrMa2RvF5dumCjkBFdX19lSNqrgKltf3aPrFMuQQKkZpK2YNuQ83hB1B3NiWzj."
-authentication.fallback-admin.user = "mailadmin"
-authentication.master.secret = "$6$4qPYDVhaUHkKcY7s$bB6qhcukb9oFNYRIvaDZgbwxrMa2RvF5dumCjkBFdX19lSNqrgKltf3aPrFMuQQKkZpK2YNuQ83hB1B3NiWzj."
-authentication.master.user = "master"
-directory.idmldap.attributes.class = "objectClass"
-directory.idmldap.attributes.description = "displayName"
-directory.idmldap.attributes.email = "mail"
-directory.idmldap.attributes.groups = "memberOf"
-directory.idmldap.attributes.name = "cn"
-directory.idmldap.attributes.secret = "userPassword"
-directory.idmldap.base-dn = "o=libregraph-idm"
-directory.idmldap.bind.auth.method = "default"
-directory.idmldap.bind.dn = "uid=reva,ou=sysusers,o=libregraph-idm"
-directory.idmldap.bind.secret = "admin"
-directory.idmldap.cache.size = 1048576
-directory.idmldap.cache.ttl.negative = "10m"
-directory.idmldap.cache.ttl.positive = "1h"
-directory.idmldap.filter.email = "(&(|(objectClass=person)(objectClass=groupOfNames))(mail=?))"
-directory.idmldap.filter.name = "(&(|(objectClass=person)(objectClass=groupOfNames))(cn=?))"
-directory.idmldap.timeout = "15s"
-directory.idmldap.tls.allow-invalid-certs = true
-directory.idmldap.tls.enable = true
-directory.idmldap.type = "ldap"
-directory.idmldap.url = "ldaps://opencloud:9235"
-directory.keycloak.auth.method = "user-token"
-directory.keycloak.cache.size = 1048576
-directory.keycloak.cache.ttl.negative = "10m"
-directory.keycloak.cache.ttl.positive = "1h"
-directory.keycloak.endpoint.method = "introspect"
-directory.keycloak.endpoint.url = "http://keycloak:8080/realms/openCloud/protocol/openid-connect/userinfo"
-directory.keycloak.fields.email = "email"
-directory.keycloak.fields.full-name = "name"
-directory.keycloak.fields.username = "preferred_username"
-directory.keycloak.timeout = "15s"
-directory.keycloak.type = "oidc"
-directory.ldap.attributes.class = "objectClass"
-directory.ldap.attributes.description = "displayName"
-directory.ldap.attributes.email = "mail"
-directory.ldap.attributes.email-alias = "mailAlias"
-directory.ldap.attributes.groups = "memberOf"
-directory.ldap.attributes.name = "uid"
-directory.ldap.attributes.secret = "userPassword"
-directory.ldap.attributes.secret-changed = "pwdChangedTime"
-directory.ldap.base-dn = "dc=opencloud,dc=eu"
-directory.ldap.bind.auth.dn = "cn=?,ou=users,dc=opencloud,dc=eu"
-directory.ldap.bind.auth.enable = true
-directory.ldap.bind.auth.search = true
-directory.ldap.bind.dn = "cn=admin,dc=opencloud,dc=eu"
-directory.ldap.bind.secret = "admin"
-directory.ldap.cache.ttl.negative = "10m"
-directory.ldap.cache.ttl.positive = "1h"
-directory.ldap.filter.email = "(&(|(objectClass=person)(objectClass=groupOfNames))(|(uid=?)(mail=?)(mailAlias=?)(cn=?)))"
-directory.ldap.filter.name = "(&(|(objectClass=person)(objectClass=groupOfNames))(|(uid=?)(cn=?)))"
-directory.ldap.timeout = "5s"
-directory.ldap.tls.allow-invalid-certs = true
-directory.ldap.tls.enable = true
-directory.ldap.type = "ldap"
-directory.ldap.url = "ldap://ldap-server:1389"
-email.encryption.enable = true
-email.encryption.append = false
-http.allowed-endpoint = 200
-http.hsts = true
-http.permissive-cors = false
-http.url = "'https://' + config_get('server.hostname')"
-http.use-x-forwarded = true
-metrics.prometheus.auth.secret = "secret"
-metrics.prometheus.auth.username = "metrics"
-metrics.prometheus.enable = true
-server.listener.http.bind = "0.0.0.0:8080"
-server.listener.http.protocol = "http"
-server.listener.https.bind = "0.0.0.0:443"
-server.listener.https.protocol = "http"
-server.listener.https.tls.implicit = true
-server.listener.imap.bind = "0.0.0.0:143"
-server.listener.imap.protocol = "imap"
-server.listener.imaptls.bind = "0.0.0.0:993"
-server.listener.imaptls.protocol = "imap"
-server.listener.imaptls.tls.implicit = true
-server.listener.pop3.bind = "0.0.0.0:110"
-server.listener.pop3.protocol = "pop3"
-server.listener.pop3s.bind = "0.0.0.0:995"
-server.listener.pop3s.protocol = "pop3"
-server.listener.pop3s.tls.implicit = true
-server.listener.sieve.bind = "0.0.0.0:4190"
-server.listener.sieve.protocol = "managesieve"
-server.listener.smtp.bind = "0.0.0.0:25"
-server.listener.smtp.protocol = "smtp"
-server.listener.submission.bind = "0.0.0.0:587"
-server.listener.submission.protocol = "smtp"
-server.listener.submissions.bind = "0.0.0.0:465"
-server.listener.submissions.protocol = "smtp"
-server.listener.submissions.tls.implicit = true
-server.max-connections = 8192
-server.socket.backlog = 1024
-server.socket.nodelay = true
-server.socket.reuse-addr = true
-server.socket.reuse-port = true
-storage.blob = "rocksdb"
-storage.data = "rocksdb"
-storage.directory = "idmldap"
-storage.fts = "rocksdb"
-storage.lookup = "rocksdb"
-store.rocksdb.compression = "lz4"
-store.rocksdb.path = "/opt/stalwart/data"
-store.rocksdb.type = "rocksdb"
-tracer.console.ansi = true
-tracer.console.buffered = true
-tracer.console.enable = true
-tracer.console.level = "trace"
-tracer.console.lossy = false
-tracer.console.multiline = false
-tracer.console.type = "stdout"
-sharing.allow-directory-query = false
diff --git a/devtools/deployments/opencloud_full/stalwart-snapshot b/devtools/deployments/opencloud_full/stalwart-snapshot
new file mode 100755
index 0000000000..f3a1b179be
--- /dev/null
+++ b/devtools/deployments/opencloud_full/stalwart-snapshot
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Snapshots the current configuration of a Stalwart >= 0.16 server.
+# Requires having stalwart-cli installed.
+set -euo pipefail
+exec stalwart-cli -k --url 'https://stalwart.opencloud.test' --user admin --password secret snapshot \
+ --include-secrets \
+ Tenant \
+ Domain \
+ Directory \
+ Authentication \
+ DkimSignature \
+ AcmeProvider \
+ Certificate \
+ DnsServer \
+ Role \
+ Authentication \
+ Account \
+ NetworkListener \
+ Tracer \
+ Sharing \
+ SystemSettings \
+ DataRetention \
+ BlobStore \
+ InMemoryStore \
+ SearchStore \
+ --allow-unresolved PublicKey \
+ "$@"
diff --git a/devtools/deployments/opencloud_full/stalwart-snapshot-passwords b/devtools/deployments/opencloud_full/stalwart-snapshot-passwords
new file mode 100755
index 0000000000..58e20d28f6
--- /dev/null
+++ b/devtools/deployments/opencloud_full/stalwart-snapshot-passwords
@@ -0,0 +1,66 @@
+#!/usr/bin/env node
+// Processes a Stalwart snapshot JSON file to perform the following:
+// - replace masked passwords with their correct value
+// - replace the defaultHostname system setting
+
+const fs = require('fs');
+const readline = require('readline');
+
+const args = process.argv.slice(2);
+const inputs = args.length > 0
+ ? args.map(file => fs.createReadStream(file))
+ : [process.stdin];
+
+async function processLines() {
+ for (const stream of inputs) {
+ const rl = readline.createInterface({
+ input: stream,
+ crlfDelay: Infinity
+ });
+
+ for await (const line of rl) {
+ const trimmed = line.trim();
+ if (!trimmed) continue;
+
+ const item = JSON.parse(trimmed);
+ const t = item['@type'];
+ const o = item['object'];
+
+ if (t === 'create' && o === 'Account') {
+ const value = item['value'] || {};
+ for (const [id, account] of Object.entries(value)) {
+ const name = account['name'];
+ if (name === 'master' || name === 'admin') {
+ const credentials = account['credentials'] || {};
+ for (const [cid, creds] of Object.entries(credentials)) {
+ if (creds['@type'] === 'Password') {
+ creds['secret'] = 'supersecret1234';
+ }
+ }
+ }
+ }
+ }
+
+ if (t === 'create' && o === 'Directory') {
+ const value = item['value'] || {};
+ for (const [id, dir] of Object.entries(value)) {
+ if (dir['@type'] === 'Ldap') {
+ if (dir['bindSecret']) {
+ dir['bindSecret']['secret'] = 'admin';
+ }
+ }
+ }
+ }
+
+ if (t === 'update' && o === 'SystemSettings') {
+ if (item['value']) {
+ item['value']['defaultHostname'] = 'stalwart.opencloud.test';
+ }
+ }
+
+ console.log(JSON.stringify(item));
+ }
+ }
+}
+
+processLines();
diff --git a/devtools/deployments/opencloud_full/stalwart.yml b/devtools/deployments/opencloud_full/stalwart.yml
index 2e3bcec326..ccc02628c2 100644
--- a/devtools/deployments/opencloud_full/stalwart.yml
+++ b/devtools/deployments/opencloud_full/stalwart.yml
@@ -7,20 +7,22 @@ services:
- ${STALWART_DOMAIN:-stalwart.opencloud.test}
stalwart:
- image: ghcr.io/stalwartlabs/stalwart:v0.15.5-alpine
+ image: ghcr.io/stalwartlabs/stalwart:v0.16.7-alpine
hostname: ${STALWART_DOMAIN:-stalwart.opencloud.test}
networks:
- opencloud-net
ports:
+ - "127.0.0.1:8443:443"
+ - "127.0.0.1:8080:8080"
- "127.0.0.1:143:143"
- "127.0.0.1:993:993"
- "127.0.0.1:1465:465"
volumes:
- - /etc/localtime:/etc/localtime:ro
- - "./config/stalwart/${STALWART_AUTH_DIRECTORY:-idmldap}.toml:/opt/stalwart/etc/config.toml"
- - stalwart-data:/opt/stalwart/data
+ - ./config/stalwart/config.json:/etc/stalwart/config.json
+ - stalwart-data:/var/lib/stalwart
environment:
- STALWART_AUTH_DIRECTORY: "${STALWART_AUTH_DIRECTORY:-idmldap}"
+ STALWART_RECOVERY_ADMIN: "admin:secret"
+ STALWART_PUBLIC_URL: "https://${STALWART_DOMAIN:-stalwart.opencloud.test}"
labels:
- "traefik.enable=true"
- "traefik.http.routers.stalwart.entrypoints=https"
@@ -30,7 +32,83 @@ services:
- "traefik.http.services.stalwart.loadbalancer.server.port=8080"
logging:
driver: ${LOG_DRIVER:-local}
- restart: always
+ restart: unless-stopped
+ entrypoint: ["/bin/sh", "-c"]
+ command:
+ - |
+ if [ ! -f /var/lib/stalwart/.initialized ]; then
+ echo "Not initialized, starting in recovery mode"
+ STALWART_RECOVERY_MODE=1 /usr/local/bin/stalwart --config /etc/stalwart/config.json &
+ pid=$$!
+ while [ ! -f /var/lib/stalwart/.initialized ]; do sleep 1; done
+ echo "Initialized, stopping recovery mode"
+ kill -TERM $$pid
+ wait $$pid
+ fi
+ echo "Starting Stalwart in production mode"
+ exec /usr/local/bin/stalwart --config /etc/stalwart/config.json
+
+ stalwart-import:
+ image: stalwart-cli:latest
+ build:
+ context: ./config/stalwart/
+ args:
+ VERSION: "1.0.8"
+ dockerfile_inline: |
+ FROM alpine:latest AS downloader
+ ARG VERSION
+ RUN apk add --no-cache curl tar xz
+ RUN arch=$(apk --print-arch) && \
+ mkdir /cli && \
+ curl --proto '=https' --tlsv1.2 -LsSf \
+ https://github.com/stalwartlabs/cli/releases/download/v$${VERSION}/stalwart-cli-$${arch}-unknown-linux-musl.tar.xz \
+ | tar xJf - --strip-components=1 -C /cli
+
+ FROM scratch
+ COPY --from=downloader /cli/stalwart-cli /stalwart-cli
+ CMD ["/stalwart-cli"]
+
+ networks:
+ - opencloud-net
+ volumes:
+ - ./config/stalwart/${STALWART_AUTH_DIRECTORY:-idmldap}.json:/snapshot.json:ro
+ - stalwart-data:/var/lib/stalwart
+ environment:
+ STALWART_URL: "http://stalwart:8080"
+ STALWART_USER: "admin"
+ STALWART_PASSWORD: "secret"
+ entrypoint: ["/bin/sh", "-c"]
+ command:
+ - |
+ if [ -f /var/lib/stalwart/.initialized ]; then
+ echo "Already initialized, skipping stalwart-cli."
+ exit 0
+ fi
+ echo "Waiting for the Stalwart recovery API to become live"
+ until wget -qO- $$STALWART_URL/healthz/live >/dev/null 2>&1; do sleep 1; done
+ echo "Applying configuration"
+ /stalwart-cli --debug apply --file /snapshot.json
+ touch /var/lib/stalwart/.initialized
+ echo "Successfully initialized"
+
+ stdin_open: true
+ tty: true
+ depends_on:
+ - stalwart
+
+ stalwart-reset:
+ image: alpine:latest
+ profiles:
+ - maintenance
+ volumes:
+ - stalwart-data:/var/lib/stalwart
+ entrypoint: ["/bin/sh", "-c"]
+ command:
+ - |
+ if [ -f /var/lib/stalwart/.initialized ]; then
+ rm -f /var/lib/stalwart/.initialized
+ echo "Success: Lockfile removed. Next time 'stalwart' boots, it will run in recovery mode."
+ fi
volumes:
stalwart-data: