From 29d9263071c2531fe12c459ab29e4329a4354de8 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sat, 19 Sep 2026 17:44:20 -0700 Subject: [PATCH] One edition: the last enterprise gates come out of shared code (SPEC 2.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every one of the 14 was `#[cfg(not(feature = "enterprise"))]` on the arm the fork always compiles: the Enterprise arms went with the import, and nothing turns the feature on. Removing the attribute leaves the same code, now unconditional, in 11 files. Two of them looked like behavior worth checking before touching: the `validate_tenant_quota` stub that always passes, and the refusal to cancel a pending DestroyAccount task. The stub is vestigial — the rebuilt multi-tenancy enforces quotas in `crates/features/src/tenancy/quota.rs` for those objects and more — and the refusal is undelete's open question, which this change leaves exactly as it was. The binary builds with no new warnings, and `system_tests` and `jmap_tests`, which cover the touched registry, task-manager and auth paths, both pass. The feature definitions stay in the manifests, inert: taking them out would widen every sync's diff for nothing. --- crates/common/src/cache/reload.rs | 1 - crates/common/src/manager/boot.rs | 1 - crates/common/src/storage/mod.rs | 1 - crates/http/src/auth/permissions.rs | 1 - crates/jmap/src/registry/get.rs | 1 - crates/jmap/src/registry/mapping/principal.rs | 2 -- crates/jmap/src/registry/mapping/task.rs | 1 - crates/jmap/src/registry/set.rs | 1 - crates/services/src/task_manager/alarm.rs | 2 -- crates/services/src/task_manager/imip.rs | 2 -- crates/services/src/task_manager/index.rs | 1 - docs/spec/SPEC.md | 8 +++++++- 12 files changed, 7 insertions(+), 15 deletions(-) diff --git a/crates/common/src/cache/reload.rs b/crates/common/src/cache/reload.rs index 2ec444b..0adb22b 100644 --- a/crates/common/src/cache/reload.rs +++ b/crates/common/src/cache/reload.rs @@ -130,7 +130,6 @@ impl Server { // Update tracers - #[cfg(not(feature = "enterprise"))] tracers.update(); // Reload queue settings diff --git a/crates/common/src/manager/boot.rs b/crates/common/src/manager/boot.rs index ae516b6..44eba80 100644 --- a/crates/common/src/manager/boot.rs +++ b/crates/common/src/manager/boot.rs @@ -157,7 +157,6 @@ impl BootManager { // Enable telemetry - #[cfg(not(feature = "enterprise"))] telemetry.enable(); if bootstrap.registry.is_bootstrap_mode() { diff --git a/crates/common/src/storage/mod.rs b/crates/common/src/storage/mod.rs index 71edc15..d14b3f5 100644 --- a/crates/common/src/storage/mod.rs +++ b/crates/common/src/storage/mod.rs @@ -98,7 +98,6 @@ impl Server { // inbuxa: BT-9: the first logo mail can carry inline; none leaves the // built-in INBUXA logo - #[cfg(not(feature = "enterprise"))] pub async fn logo_resource( &self, domain: &str, diff --git a/crates/http/src/auth/permissions.rs b/crates/http/src/auth/permissions.rs index fd86613..475ed33 100644 --- a/crates/http/src/auth/permissions.rs +++ b/crates/http/src/auth/permissions.rs @@ -35,7 +35,6 @@ impl AccountApiHandler for Server { &self, access_token: &AccessToken, ) -> trc::Result { - #[cfg(not(feature = "enterprise"))] let edition = "oss"; diff --git a/crates/jmap/src/registry/get.rs b/crates/jmap/src/registry/get.rs index d9a4f48..80ac945 100644 --- a/crates/jmap/src/registry/get.rs +++ b/crates/jmap/src/registry/get.rs @@ -397,7 +397,6 @@ impl RegistryGet for Server { ObjectType::ArchivedItem => crate::inbuxa::undelete::get(get) .await .map(|get| get.into_response()), - #[cfg(not(feature = "enterprise"))] #[allow(unreachable_patterns)] // inbuxa: every object type has an arm now _ => Ok(get.not_found_any().into_response()), } diff --git a/crates/jmap/src/registry/mapping/principal.rs b/crates/jmap/src/registry/mapping/principal.rs index 10b9813..5922951 100644 --- a/crates/jmap/src/registry/mapping/principal.rs +++ b/crates/jmap/src/registry/mapping/principal.rs @@ -345,7 +345,6 @@ pub(crate) async fn validate_role( } -#[cfg(not(feature = "enterprise"))] pub async fn validate_tenant_quota( _server: &Server, _access_token: &AccessToken, @@ -360,7 +359,6 @@ pub async fn schedule_account_destruction( account: &Account, ) -> trc::Result<()> { - #[cfg(not(feature = "enterprise"))] let status = TaskStatus::now(); let (account_domain_id, account_name, account_type) = match account { diff --git a/crates/jmap/src/registry/mapping/task.rs b/crates/jmap/src/registry/mapping/task.rs index 9669c6c..d56d3bc 100644 --- a/crates/jmap/src/registry/mapping/task.rs +++ b/crates/jmap/src/registry/mapping/task.rs @@ -264,7 +264,6 @@ pub(crate) async fn task_set( let due = task.due_timestamp(); - #[cfg(not(feature = "enterprise"))] if let Task::DestroyAccount(_) = task { set.response.not_destroyed.append( id, diff --git a/crates/jmap/src/registry/set.rs b/crates/jmap/src/registry/set.rs index a01f46a..9136268 100644 --- a/crates/jmap/src/registry/set.rs +++ b/crates/jmap/src/registry/set.rs @@ -907,7 +907,6 @@ impl RegistrySet for Server { set.fail_all_destroy("Telemetry objects cannot be deleted"); Ok(set.into_response()) } - #[cfg(not(feature = "enterprise"))] #[allow(unreachable_patterns)] // inbuxa: ArchivedItem was the last one _ => { set.fail_all_create("Enterprise objects cannot be created"); diff --git a/crates/services/src/task_manager/alarm.rs b/crates/services/src/task_manager/alarm.rs index 45701da..8921175 100644 --- a/crates/services/src/task_manager/alarm.rs +++ b/crates/services/src/task_manager/alarm.rs @@ -554,7 +554,6 @@ async fn build_template( // inbuxa: BT-11, BT-18: the operator's template, read now, else the built-in - #[cfg(not(feature = "enterprise"))] let custom = inbuxa_features::branding::templates::stored( server.registry(), inbuxa_features::branding::templates::Which::Alarm, @@ -562,7 +561,6 @@ async fn build_template( .await .ok() .flatten(); - #[cfg(not(feature = "enterprise"))] let template = custom.as_ref().unwrap_or(&server.core.groupware.alarms_template); let formatter = TextFormatter::new(account_info.locale().as_str())?; let locale = formatter.locale; diff --git a/crates/services/src/task_manager/imip.rs b/crates/services/src/task_manager/imip.rs index 28b53bb..c09e2ad 100644 --- a/crates/services/src/task_manager/imip.rs +++ b/crates/services/src/task_manager/imip.rs @@ -324,7 +324,6 @@ pub async fn build_itip_template( logo_cid: &str, ) -> trc::Result
{ // inbuxa: BT-11, BT-18: the operator's template, read now, else the built-in - #[cfg(not(feature = "enterprise"))] let custom = inbuxa_features::branding::templates::stored( server.registry(), inbuxa_features::branding::templates::Which::Invite, @@ -332,7 +331,6 @@ pub async fn build_itip_template( .await .ok() .flatten(); - #[cfg(not(feature = "enterprise"))] let template = custom.as_ref().unwrap_or(&server.core.groupware.itip_template); let formatter = TextFormatter::new(account_info.locale().as_str())?; let locale = formatter.locale; diff --git a/crates/services/src/task_manager/index.rs b/crates/services/src/task_manager/index.rs index 603058e..39533f8 100644 --- a/crates/services/src/task_manager/index.rs +++ b/crates/services/src/task_manager/index.rs @@ -581,7 +581,6 @@ async fn build_contact_document( } -#[cfg(not(feature = "enterprise"))] // inbuxa: MON-16: a trace's search document, when trace search is on: // its event types, queue ids, and addresses, their domains, hosts, IPs, // message ids and account names as keywords diff --git a/docs/spec/SPEC.md b/docs/spec/SPEC.md index cdccb7a..39f7aef 100644 --- a/docs/spec/SPEC.md +++ b/docs/spec/SPEC.md @@ -178,7 +178,13 @@ checks already are: 30 checks across 18 files at the commit above. That keeps each sync's conflicts few and predictable. The `enterprise` Cargo feature and every edition check are removed. There is -one edition. +one edition. Done for the code on 2026-09-19: the last 14 gates, all of them +`#[cfg(not(feature = "enterprise"))]` on the path the fork always takes, came +off across 11 files, and no `is_enterprise_edition()` check survived the +import. The feature's *definitions* stay in the manifests, inert now that +nothing turns them on and nothing reads them, because removing them would +widen every sync's diff for no gain (§2.2). An import that changes a gated +file brings its gates back in the merge; they come off again with it. One exception: scale-out storage lives in new AGPL files inside `crates/store` (`backend/scaleout/`), because the rebuilt-features crate