6.5 KiB
Crate Boundaries And Migration Guardrails
Use this when: you add a crate dependency, move code across crates, touch a storage_api.rs boundary file, or need the change-type vocabulary the architecture guard enforces.
Source of truth: scripts/check_architecture_migration_rules.sh (the enumerated rules; this file is its boundary document) and scripts/check_layer_dependencies.sh (layer and edge checks). Extend those guards instead of adding a parallel system.
PR Types
Every PR must declare exactly one type:
docs-onlytest-onlycontractapi-extractionpure-moveconsumer-migrationdependency-migrationsecurity-changebehavior-changeci-gate
Do not mix directory movement, security tightening, and behavior changes in one PR.
Dependency Direction
Contract crates stay below implementation crates. Forbidden edges:
| Edge | Why |
|---|---|
storage-api -> ecstore |
Storage contracts must not depend on the storage implementation |
security-governance -> rustfs |
Governance contracts stay below the binary crate |
extension-schema -> rustfs |
The extension schema is consumed by the binary, never the reverse |
extension-schema -> ecstore |
The extension schema must not reach storage internals |
rustfs-storage-apiexposes storage-facing replication status/state contracts only throughcrates/storage-api/src/replication.rs, so its temporary dependency onrustfs-filemetawire types stays centralized and norustfs-replication/rustfs-storage-apicycle appears.- Leaf crates (
config,credentials,crypto,io-metrics,madmin) may not depend on otherrustfs-*crates, in either TOML spelling, except the adjudicated edges pinned in the guard's leaf allowlist:io-metrics -> rustfs-s3-ops(pure contract crates sharing theS3Operationvocabulary) andmadmin -> rustfs-signer(the SigV4-signed admin SDK client). A new leaf exception must be a pure contract dependency (types and enums only, no I/O, no globals, no non-contract internal dependencies) and land together with its allowlist entry. - Compile-time source reads follow the same direction:
include_str!/include!of a.rsfile must not resolve outside the including crate (scripts/check_layer_dependencies.sh). Shared source-text expectations belong in a contract surface such asrustfs_protos::compat_manifest(crates/protos/src/compat_manifest.rs) and are asserted by each owning crate.
ECStore Access Boundary
Outer crates reach ECStore only through rustfs_ecstore::api, and only from one local boundary file per owner (storage_api.rs). Boundary files and facade groups are inventoried in ecstore-api-facade-inventory.md.
- Inside a boundary file, raw
rustfs_ecstore::api::...paths are centralized behind localecstore_*module aliases; code outside the boundary sees local type aliases, constants, traits, or wrapper functions, never the raw facade path. - Non-trait ECStore surfaces (metadata, object-lock, lifecycle journal, monitor, notification types) stay behind local aliases; boundary function signatures do not expose raw ECStore facade types once narrowed. Object and error aliases anchor on storage-api associated object types and a local
StorageError. - Outer consumers use
rustfs-storage-apioperation traits (ObjectIO,ObjectOperations,ListOperations,MultipartOperations,HealOperations,NamespaceLocking) and generic list responses (ListObjectsV2Info,ListObjectVersionsInfo,ObjectInfoOrErr) directly; ECStore keeps concrete aliases only for internal implementation and compatibility. - Bucket lifecycle, replication, versioning, object-lock, restore-request, disk, RPC peer client, and warm-backend trait methods are reached through owner-local compatibility traits or wrapper functions, not by importing ECStore traits outside the boundary.
- The old
StorageAPIaggregate facade must not reappear in productioncrates/ecstore/srcorrustfs/srccode. - Facade-covered ECStore root modules (layout,
endpoints,disks_layout, bitrot, erasure, object DTO/reader, event, list, batch processor,global) stay crate-private; public access goes through the matchingrustfs_ecstore::api::*group. - Cluster control-plane read models stay owned by the crate-private
clustermodule and are published throughrustfs_ecstore::api::cluster; pool-state, local-node storage, and peer-health projections are read-only. - RustFS startup internals are crate-private: only
startup_entrypointis a public startup module of therustfslibrary (rustfs/src/lib.rs), and items inside the otherstartup_*modules use crate visibility. - The observability dependency baseline is obs-ecstore-dependency-inventory.md; observability extraction updates it together with the guard.
Loss-Prevention Coverage
The guard pins specific public re-export lines (its require_source_line entries) so contract surfaces cannot silently disappear during cleanup. The canonical lists are the guard script and the owning files, not this page:
crates/storage-api/src/lib.rs: admin, bucket, capability, error, multipart, observability, object, and topology contract re-exports;crates/concurrency/src/lib.rs: workload admission contract re-exports;rustfs/src/lib.rs:pub mod startup_entrypoint;.
ECStore keeps compile-time coverage for StorageAdminApi, HealOperations, and the separate NamespaceLocking operation group (crates/ecstore/tests/ecstore_contract_compat_test.rs), and its internal consumers use the rustfs-storage-api lifecycle DTOs ExpirationOptions and TransitionedObject directly.
Temporary Compatibility Code
Every temporary compatibility path carries a RUSTFS_COMPAT_TODO(<id>) source marker with a removal condition and a matching entry in compat-cleanup-register.md; the guard enforces the match in both directions. Compatibility layers are deleted in their own cleanup change, never bundled with new migration logic.
Config Model
The server-config model (Config, KV, KVS) and the global server-config snapshot accessors are owned by rustfs_config::server_config; ECStore keeps persistence, storage-class state, and startup wiring, and its public facades must not re-export those symbols. See config-model-boundary-adr.md.
Required Architecture Documents
The guard requires the documents and section headings listed in its require_source_contains entries (scripts/check_architecture_migration_rules.sh); the directory index is README.md.