Quick fixes from the first boot: recovery admin, upsell, warnings

- The recovery administrator (INBUXA_RECOVERY_ADMIN, or STALWART_RECOVERY_ADMIN)
  is honored only in bootstrap and recovery mode. On a configured server it's
  ignored with a startup warning. Before, it was a standing full-admin login
  for as long as the variable stayed set.
- The Enterprise upsell error is replaced by "This feature isn't available in
  INBUXA yet" for the features still to be rebuilt.
- Workspace warnings: 25 to 0. cargo fix removed the unused imports. The
  seven places where Enterprise code used to plug in keep their parameters,
  each with an inbuxa: comment naming the rebuild that uses it again. The
  antispam test's mock-server imports are back behind pending-rebuild.
This commit is contained in:
2026-09-18 11:36:44 -07:00
parent d3f0b36dd2
commit c9c761fab5
16 changed files with 51 additions and 30 deletions
+4 -2
View File
@@ -514,13 +514,15 @@ impl Server {
pub async fn get_directory_for_domain(
&self,
domain_name: &str,
// inbuxa: unused until per-domain directories (Domain.directoryId) are rebuilt; see docs/spec/SPEC.md §4
_domain_name: &str,
) -> trc::Result<Option<&Arc<Directory>>> {
Ok(self.get_default_directory())
}
pub fn get_directory_for_cached_domain(&self, domain: &DomainCache) -> Option<&Arc<Directory>> {
// inbuxa: `_domain` is unused until per-domain directories (Domain.directoryId) are rebuilt
pub fn get_directory_for_cached_domain(&self, _domain: &DomainCache) -> Option<&Arc<Directory>> {
self.get_default_directory()
}
+2 -1
View File
@@ -45,7 +45,8 @@ impl Server {
&self,
permissions: &structs::Permissions,
role_ids: &[Id],
tenant_id: Option<u32>,
// inbuxa: unused until multi-tenancy is rebuilt: the tenant permission ceiling (docs/spec/features/multi-tenancy.md MT-13)
_tenant_id: Option<u32>,
) -> trc::Result<PermissionsGroup> {
// Calculate effective permissions
let (mut permissions, roles) = match permissions {
-1
View File
@@ -17,7 +17,6 @@ use registry::{
},
types::id::ObjectId,
};
use store::{registry::RegistryQuery, roaring::RoaringBitmap};
use types::id::Id;
#[derive(Debug, Default)]
+2 -2
View File
@@ -28,7 +28,7 @@ use registry::{
enums::{DkimRotationStage, Locale, StorageQuota, TenantStorageQuota},
prelude::{ObjectType, Property},
structs::{
Account, DkimSignature, Domain, EncryptionAtRest, MailingList, MaskedEmail,
Account, DkimSignature, Domain, EncryptionAtRest, MailingList,
Permissions, PublicKey, Role, SubAddressing, Tenant,
},
},
@@ -38,7 +38,7 @@ use std::{borrow::Cow, sync::Arc};
use store::{
U64_LEN,
registry::{RegistryQuery, bootstrap::Bootstrap},
write::{key::KeySerializer, now},
write::key::KeySerializer,
};
use trc::{AddContext, StoreEvent};
use types::id::Id;
+1 -1
View File
@@ -38,7 +38,7 @@ pub mod storage;
pub mod telemetry;
impl Core {
pub async fn parse(bp: &mut Bootstrap, mut storage: Storage) -> Self {
pub async fn parse(bp: &mut Bootstrap, storage: Storage) -> Self {
Self {
sieve: Scripting::parse(bp).await,
+2 -1
View File
@@ -141,7 +141,8 @@ impl Telemetry {
}
impl Tracers {
pub async fn parse(bp: &mut Bootstrap, storage: &Storage) -> Self {
// inbuxa: `_storage` is unused until monitoring history (stored traces and metrics) is rebuilt
pub async fn parse(bp: &mut Bootstrap, _storage: &Storage) -> Self {
let mut custom_levels = AHashMap::new();
let mut tracers: Vec<TelemetrySubscriber> = Vec::new();
let mut global_interests = Interests::default();
+3
View File
@@ -161,6 +161,9 @@ pub struct Data {
pub struct LogoCache {
domain_id: u32,
tenant_id: Option<u32>,
// inbuxa: read again when the /logo endpoint (per-tenant and per-domain
// branding) is rebuilt; docs/spec/features/multi-tenancy.md MT-22.
#[allow(dead_code)]
data: Option<Resource<Vec<u8>>>,
}
+2 -3
View File
@@ -23,7 +23,7 @@ use crate::{
};
use directory::Recipient;
use mail_auth::IpLookupStrategy;
use registry::schema::{enums::ExpressionVariable, structs::MaskedEmail};
use registry::schema::enums::ExpressionVariable;
use sieve::Sieve;
use std::{
borrow::Cow,
@@ -32,10 +32,9 @@ use std::{
};
use store::{
Deserialize, IterateParams, ValueKey,
write::{AlignedBytes, Archive, QueueClass, ValueClass, now},
write::{AlignedBytes, Archive, QueueClass, ValueClass},
};
use trc::{AddContext, SpamEvent};
use types::id::Id;
impl Server {
pub async fn rcpt_resolve(
@@ -4,9 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use sieve::{FunctionMap, compiler::Number, runtime::Variable};
use std::time::Instant;
use trc::{AiEvent, SecurityEvent};
use sieve::{FunctionMap, runtime::Variable};
use super::PluginContext;
@@ -14,7 +12,8 @@ pub fn register(plugin_id: u32, fnc_map: &mut FunctionMap) {
fnc_map.set_external_function("llm_prompt", plugin_id, 3);
}
pub async fn exec(ctx: PluginContext<'_>) -> trc::Result<Variable> {
// inbuxa: the LLM Sieve function is a no-op until AI classification is rebuilt
pub async fn exec(_ctx: PluginContext<'_>) -> trc::Result<Variable> {
Ok(false.into())
}
+2 -1
View File
@@ -96,7 +96,8 @@ impl Telemetry {
}
impl TelemetrySubscriberType {
pub fn spawn(self, builder: SubscriberBuilder, is_enterprise: bool) {
// inbuxa: `_is_enterprise` is unused until monitoring history is rebuilt, and goes when it is: there is one edition
pub fn spawn(self, builder: SubscriberBuilder, _is_enterprise: bool) {
match self {
TelemetrySubscriberType::ConsoleTracer(settings) => {
spawn_console_tracer(builder, settings)
@@ -20,10 +20,7 @@ use registry::{
},
types::EnumImpl,
};
use store::{
registry::{RegistryObjectCounter, RegistryQuery},
write::{BatchBuilder, RegistryClass, ValueClass, now},
};
use store::write::{BatchBuilder, RegistryClass, ValueClass, now};
use trc::AddContext;
use types::id::Id;
@@ -22,7 +22,7 @@ use registry::{
jmap::{IntoValue, JsonPointerPatch, RegistryJsonPatch},
schema::{
enums::{DeliveryErrorType, MessageFlag, RecipientFlag},
prelude::{ObjectType, Property},
prelude::Property,
structs::{
DeliveryError, QueueExpiry, QueueExpiryAttempts, QueueExpiryTtl, QueuedMessage,
QueuedRecipient, RecipientStatus, ServerResponse,
@@ -40,7 +40,7 @@ use std::str::FromStr;
use store::{
Deserialize, IterateParams, U64_LEN, ValueKey,
ahash::AHashSet,
registry::{RegistryFilterOp, RegistryQuery},
registry::RegistryFilterOp,
write::{AlignedBytes, Archive, QueueClass, ValueClass, key::DeserializeBigEndian, now},
};
use trc::AddContext;
+6 -2
View File
@@ -29,9 +29,13 @@ impl EnterpriseRegistry for Server {
}
// These are the Enterprise features INBUXA hasn't rebuilt yet
// (docs/spec/SPEC.md §4). Each type leaves this list when its rebuild
// lands. There's no edition to upgrade to, so the message says so.
Err(trc::JmapEvent::Forbidden.into_err().details(concat!(
"This feature is only available in the Enterprise edition. ",
"Obtain your trial license at https://license.stalw.art/trial."
"This feature isn't available in ",
types::brand!(),
" yet."
)))
}
}
@@ -11,7 +11,6 @@ use groupware::{cache::GroupwareCache, calendar::CalendarEvent, contact::Contact
use registry::{
schema::{
enums::IndexDocumentType,
prelude::{ObjectType, Property},
structs::{TaskIndexDocument, TaskIndexTrace, TaskStatus},
},
types::EnumImpl,
@@ -29,7 +28,6 @@ use store::{
};
use trc::{AddContext, TaskManagerEvent};
use types::{
blob_hash::BlobHash,
collection::{Collection, SyncCollection},
field::EmailField,
};
+15 -1
View File
@@ -18,7 +18,21 @@ impl RegistryStore {
// Build store
inner.store = match inner.read_data_store().await {
RegistryInit::Ok(data_store) => Store::build(data_store).await?,
RegistryInit::Ok(data_store) => {
// The recovery administrator exists for first-boot setup and
// for recovery mode. On a configured server running normally
// it would be a standing full-admin login for whoever knows the
// variable, so it's ignored there, and loudly.
if !inner.env_recovery_mode && inner.env_recovery_admin.take().is_some() {
eprintln!();
eprintln!("⚠️ INBUXA_RECOVERY_ADMIN (or STALWART_RECOVERY_ADMIN) is set, but the");
eprintln!(" server is configured and not in recovery mode, so it is ignored.");
eprintln!(" Remove it from the environment. To use it for recovery, also set");
eprintln!(" INBUXA_RECOVERY_MODE=1.");
eprintln!();
}
Store::build(data_store).await?
}
RegistryInit::Err(err) => return Err(err),
RegistryInit::Bootstrap => {
inner.env_recovery_mode = true;