Files
rustfs/docs/architecture/crate-boundaries.md
T

14 KiB

Crate Boundaries And Migration Guardrails

These rules apply to architecture-migration PRs linked to rustfs/backlog#660.

PR Types

Every PR must declare exactly one type:

  • docs-only
  • test-only
  • contract
  • api-extraction
  • pure-move
  • consumer-migration
  • dependency-migration
  • security-change
  • behavior-change
  • ci-gate

Do not mix directory movement, security tightening, and behavior changes in one PR.

Dependency Direction

Contract crates must stay below implementation crates. Initial forbidden edges:

  • storage-api -> ecstore
  • security-governance -> rustfs
  • extension-schema -> rustfs
  • extension-schema -> ecstore

Existing migration checks live in:

  • scripts/check_layer_dependencies.sh
  • scripts/check_architecture_migration_rules.sh

Extend these guardrails instead of adding a parallel system.

Required Architecture Documents

The migration guard must keep these baseline documents present and anchored to their required sections:

  • docs/architecture/overview.md: Baseline, Core Principle, Phase Order.
  • docs/architecture/runtime-lifecycle.md: Startup And Readiness, Shutdown Lifecycle Boundary, AppContext Foundation.
  • docs/architecture/storage-control-data-plane.md: Storage API Contracts, Cluster Control Plane, Background Controllers.
  • docs/architecture/crate-boundaries.md: PR Types, Dependency Direction, Required Architecture Documents.
  • docs/architecture/readiness-matrix.md: Request Behavior Matrix, Runtime Dependency Matrix, Probe Semantics.
  • docs/architecture/global-state-crate-split-plan.md: Remaining Global Owners, Runtime Source Boundaries, Fallback Removal Plan, Crate Split Evaluation.

Pre-Push Expert Review

Before pushing any PR branch, record three expert reviews in the task notes:

Expert Required focus
Quality/architecture Structure, naming, dependency direction, PR type, scope, and over-abstraction risk
Migration preservation Startup order, readiness, quorum, reader semantics, AppContext/global fallback, notify/audit lifecycle, IAM/KMS boundaries, and compatibility
Testing/verification Focused tests, regression tests, commands run, missing coverage, and whether tests are forcing business-logic drift

Push is allowed only when all three experts return pass or pass-with-nonblocking-follow-up. Any blocker prevents push until the issue is fixed and the relevant review is repeated.

Temporary Compatibility Code

Temporary compatibility code that must be removed later must include a searchable source comment and a cleanup-register entry.

Use this source-comment format:

// RUSTFS_COMPAT_TODO(API-005): keep old ecstore::store_api path during storage-api migration. Remove after all consumers use rustfs-storage-api.

Rules:

  • Add the marker only to temporary compatibility paths, not permanent APIs.
  • Include the task ID in the marker.
  • State why the compatibility path exists and when it can be removed.
  • Use this for temporary re-exports, wrappers, fallbacks, legacy action mappings, and old endpoint compatibility layers.
  • Delete compatibility layers in their own cleanup PR.

Config Model First

ecstore::config::{Config, KV, KVS} should move before extension config adapters or config-schema work. First inventory consumers, then decide whether existing crates/config is enough or whether a smaller model crate is required.

The current decision is recorded in config-model-boundary-adr.md: use the existing rustfs-config package for the pure server-config model and global server-config snapshot accessors, while ECStore keeps config persistence, storage-class global state, default wiring, and startup initialization.

The old rustfs_ecstore::config::{Config, KV, KVS, register_default_kvs, get_global_server_config, set_global_server_config} compatibility path must not be restored after the Phase 1a cleanup. Consumers use rustfs_config::server_config for the moved model and accessors; ECStore public facades must not re-export those symbols.

Loss-Prevention Coverage

Architecture migration checks must keep public contract re-exports and ECStore compatibility coverage from silently drifting during cleanup PRs.

Required rustfs-storage-api public re-exports:

  • pub use admin::{DiskSetSelector, StorageAdminApi};
  • pub use bucket::{BucketInfo, BucketOperations, BucketOptions, DeleteBucketOptions, MakeBucketOptions, SRBucketDeleteOp};
  • pub use capability::{CapabilitySnapshotError, CapabilityState, CapabilityStatus};
  • pub use error::{StorageErrorCode, StorageResult};
  • pub use multipart::{CompletePart, ListMultipartsInfo, ListPartsInfo, MultipartInfo, MultipartUploadResult, PartInfo};
  • pub use observability::{MemorySamplingState, ObservabilitySnapshot, ObservabilitySnapshotProvider, PlatformSupport, UserspaceProfilingCapability};
  • pub use object::{HTTPPreconditions, HTTPRangeError, HTTPRangeSpec, ObjectLockRetentionOptions};
  • pub use object::{ExpirationOptions, TransitionedObject};
  • pub use object::{HealOperations, MultipartOperations, NamespaceLocking, ObjectIO, ObjectOperations};
  • pub use object::{ListObjectVersionsInfo, ListObjectsInfo, ListObjectsV2Info, ListOperations, ObjectInfoOrErr};
  • pub use object::{ObjectPreconditionError, ObjectPreconditionPart, ObjectPreconditionState};
  • pub use object::{VersionMarker, WalkOptions, WalkVersionsSortOrder};
  • pub use topology::{DiskCapabilities, TopologyCapabilities, TopologyDisk, TopologyLabels, TopologyPool, TopologySet, TopologySnapshot, TopologySnapshotProvider};

Required rustfs-concurrency public workload admission contract re-exports:

  • pub use workload::{AdmissionState, WorkloadAdmissionRegistrySnapshot, WorkloadAdmissionSnapshot, WorkloadAdmissionSnapshotProvider, WorkloadClass};

ECStore must keep compile-time coverage for StorageAdminApi, HealOperations, and the separate NamespaceLocking operation group.

The old StorageAPI aggregate facade must not reappear in production crates/ecstore/src or rustfs/src code after the storage operation groups have been made explicit.

Outer RustFS/IAM consumers must use rustfs-storage-api generic list response contracts directly for ListObjectsV2Info, ListObjectVersionsInfo, and ObjectInfoOrErr; ECStore keeps the concrete aliases only for internal implementation and compatibility.

Outer RustFS/scanner consumers must use rustfs-storage-api operation traits directly for ObjectIO, ObjectOperations, ListOperations, MultipartOperations, HealOperations, and NamespaceLocking; ECStore keeps the concrete compatibility traits only for internal implementation and downstream compatibility. Outer consumers must not import ECStore directly outside compatibility boundaries except for temporary trait imports needed for method resolution or local test trait implementations. Non-trait ECStore surfaces must stay behind local aliases, constants, or wrapper functions.

Outer compatibility boundary modules must use rustfs_ecstore::api for ECStore public facade surfaces such as layout, storage owner, admin, metrics, notification, capacity, bucket/config helpers, disk/error contracts, global state accessors, RPC constants/clients, reader helpers, tier helpers, and rebalance status contracts. Any non-ECStore storage_compat.rs import from rustfs_ecstore must route through the rustfs_ecstore::api facade. The legacy ECStore root endpoints and disks_layout compatibility modules must remain crate-private; public layout access goes through rustfs_ecstore::api::layout. Facade-covered ECStore root modules must remain crate-private after this boundary is established; outer crates should use rustfs_ecstore::api::* instead of legacy root module paths. This includes storage/layout surfaces as well as remaining bitrot, erasure coding, object DTO/reader, event, list, and batch processor root modules once their facade groups exist. ECStore root global re-exports must also stay removed once consumers use rustfs_ecstore::api::global or crate-internal crate::global paths. RustFS root storage_compat.rs must expose bucket metadata and quota contracts as explicit aliases only. Broad metadata, metadata_sys, and quota module passthroughs are reserved to narrower app/admin/storage compatibility boundaries that still need module-local owner cleanup. Root runtime storage config initialization and disk endpoint contracts must also stay explicit aliases. The root compatibility boundary must not restore com, bare init, or grouped endpoint::Endpoint passthroughs. RustFS root storage_compat.rs must not re-export ECStore API symbols directly; remaining root runtime compatibility symbols must be local type aliases, constants, traits, or wrapper functions so ownership stays visible at the boundary. RustFS admin storage_compat.rs must expose config IO and default initialization through explicit aliases. The admin compatibility boundary must not restore broad com or bare init passthroughs. RustFS admin and app storage_compat.rs bucket-facing compatibility contracts must stay explicitly whitelisted. They must not restore broad bucket module, client object API, client transition API, or storage-class module passthroughs once a local compatibility boundary has narrowed them to specific aliases. RustFS storage storage_compat.rs must expose bucket metadata, object-lock, policy, replication, tagging, versioning, object API, and test-only storage-class config contracts through explicit aliases. The storage compatibility boundary must not restore broad metadata, metadata_sys, object_lock, policy_sys, replication, tagging, utils, versioning, versioning_sys, object_api_utils, or com passthroughs. RustFS storage owner storage_compat.rs must not re-export ECStore API symbols directly except temporary trait imports needed for method resolution. Remaining storage-owner compatibility symbols must be local constants, type aliases, or wrapper functions so storage-owned global state and helper access stays visible at the boundary. RustFS app, admin, and storage outer storage_compat.rs object and error facade aliases must stay anchored on storage-api associated object types and local StorageError aliases. They must not reintroduce raw rustfs_ecstore::api::object::{ObjectInfo,ObjectOptions} or rustfs_ecstore::api::error::{Error,Result} references. Outer compatibility function signatures must also use local aliases for ECStore metadata, object-lock, lifecycle journal, monitor, and notification facade types. The boundary may define the local alias, but call signatures must not expose the raw ECStore facade path once narrowed. The RustFS storage owner compatibility boundary must keep raw ECStore facade paths centralized behind local ecstore_* module aliases rather than scattering rustfs_ecstore::api::... references through its aliases and wrappers. The RustFS app/admin storage compatibility boundaries must likewise route raw ECStore facade access through their local ecstore_* module aliases instead of scattering rustfs_ecstore::api::... paths through compatibility wrappers. Peripheral consumer storage compatibility boundaries must follow the same pattern. IAM, heal, scanner, notify, observability, Swift, S3 Select, test, and fuzz storage compatibility modules keep raw ECStore facade access centralized behind local ecstore_* module aliases. RustFS root runtime and e2e storage compatibility boundaries must follow the same pattern, keeping raw ECStore facade access centralized behind local ecstore_* module aliases. Outer bucket lifecycle, replication, versioning, object-lock, and restore-request trait method access must stay behind local compatibility traits or wrapper functions. Non-compat sources must not import those ECStore bucket API traits directly after the wrapper boundary is established. Disk, RPC peer client, and warm-backend method-resolution access must follow the same pattern: non-compat sources use owner-local compatibility traits or test aliases instead of importing ECStore traits directly. Scanner, notify, observability, and e2e storage_compat.rs boundaries must also stay narrow. Scanner must not restore grouped bucket compatibility exports for target, lifecycle, metadata, replication, or versioning modules. Notify must not restore broad config/global module imports. Observability must consume data usage through a local DTO projection instead of re-exporting the ECStore data-usage loader. The e2e harness must not restore grouped RPC passthroughs. Test and fuzz storage_compat.rs harnesses must also stay narrow. Heal and scanner test harnesses must expose ECStore contracts through direct aliases or local wrappers, and fuzz harnesses must wrap bucket utility entrypoints instead of restoring grouped ECStore passthrough exports. External ECStore API facade imports must stay inside local storage_api boundary files after the external runtime, test, and fuzz consumers have been narrowed. IAM, heal, scanner, notify, observability, Swift, S3 Select, e2e, and fuzz code must not reintroduce direct rustfs_ecstore::api::... references outside those boundary files. The observability ECStore dependency baseline is tracked in obs-ecstore-dependency-inventory.md; future observability extraction PRs must update that inventory with the guard.

ECStore ClusterControlPlane read models must stay owned by the crate-private cluster module. Public access goes through rustfs_ecstore::api::cluster so outer crates cannot depend on ECStore root control-plane internals. Pool-state, local-node storage, and peer-health status projections are part of the same facade boundary and must remain read-only until a later controller slice explicitly wires dynamic health or membership behavior.

RustFS startup internals must stay crate-private after the startup owner split. Only startup_entrypoint remains a public startup module for the binary entrypoint; IAM bootstrap, optional runtime, and profiling startup shims must not be re-exported as public library modules. Items inside crate-private startup modules must also use crate visibility rather than bare public visibility.

ECStore internal consumers must use rustfs-storage-api lifecycle helper DTOs directly for ExpirationOptions and TransitionedObject; ECStore keeps the old lifecycle paths only as downstream compatibility re-exports.