# Config Model Boundary ADR

Related issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)

Task: `CFG-002`

## Decision

Use the existing `crates/config` package (`rustfs-config`) as the target owner
for the pure server-config model. Do not create a new config-model crate for
the first extraction.

The next model extraction PR should introduce the model under:

```text
crates/config/src/server_config.rs
```

The exported path should be:

```rust
rustfs_config::server_config::{Config, KV, KVS}
```

The extraction kept the existing path available through a temporary
compatibility re-export:

```rust
rustfs_ecstore::config::{Config, KV, KVS}
```

That re-export included `RUSTFS_COMPAT_TODO(CFG-004)` and a matching entry in
[`compat-cleanup-register.md`](compat-cleanup-register.md) until the model
consumers were migrated. The CFG-004 cleanup removed this old model path after
code scans showed consumers import the model directly from `rustfs-config`.

Follow-up `CFG-008` moved the process-global server-config snapshot accessors
to `rustfs_config::server_config` after the model path stabilized. Its temporary
`rustfs_ecstore::config::{get_global_server_config, set_global_server_config}`
compatibility re-export was removed after in-repo runtime consumers migrated to
the `rustfs-config` owner.

## Why `rustfs-config`

`rustfs-config` is already the lowest RustFS crate for configuration constants
and subsystem identifiers used by ECStore, notify, audit, targets, scanner, IAM,
and admin code. The current `ecstore::config::{Config, KV, KVS}` model already
uses `rustfs-config` constants, so moving the pure model upward to
`rustfs-config` cuts the wrong dependency direction without adding another crate.

Creating a new crate now would add a second config namespace before consumers
are migrated. That would increase re-export and compatibility surface while not
removing any storage or runtime dependency by itself.

## Allowed Dependencies

The server-config model module may use only:

- `std::collections::HashMap`
- `std::sync::{LazyLock, OnceLock, RwLock}` for the default `KVS` registration
  surface and process-global server-config snapshot
- `serde` for `KV` and `KVS` serialization compatibility
- `serde_json` for `Config::marshal` and `Config::unmarshal`
- existing `rustfs-config` constants and subsystem modules

If `serde` and `serde_json` are added to `rustfs-config`, they should be attached
only to a model feature such as `server-config-model` unless the implementation
PR proves that making them non-optional is simpler and harmless for downstream
builds.

## Forbidden Dependencies

The model module must not depend on:

- `rustfs-ecstore`
- `rustfs`
- `StorageAPI` or object persistence helpers
- notify, audit, targets, IAM, scanner, KMS, or admin handler crates
- async runtimes, HTTP/router crates, object-store crates, or runtime lifecycle
  state
- unrelated runtime global state outside the process-global server-config
  snapshot
- `ConfigSys`, `read_config_without_migrate`, `save_server_config`, or any
  `com.rs` persistence helper

## Boundary Split

Move in the first extraction:

- `KV`
- `KVS`
- `Config`
- `DEFAULT_KVS`
- `register_default_kvs`
- `Config::new`
- `Config::get_value`
- `Config::set_defaults`
- `Config::marshal`
- `Config::unmarshal`
- `Config::merge`

Keep in `ecstore`:

- `ConfigSys`
- `init_global_config_sys`
- `try_migrate_server_config`
- `read_config_without_migrate`
- `save_server_config`
- generic `com.rs` config-object helpers
- storage-class runtime global state

Keep default registration wiring in `ecstore::config::init` until a later PR
extracts a dedicated default-registration contract. The values may be registered
through the moved `rustfs_config::server_config::register_default_kvs`, but the
startup order and caller remain unchanged.

Move in `CFG-008`:

- `GLOBAL_SERVER_CONFIG`
- `get_global_server_config`
- `set_global_server_config`

The temporary ECStore compatibility re-export for these accessors was removed
after code scans showed in-repo consumers use `rustfs_config::server_config`
directly.

## Required Shape Preservation

The extraction PR must preserve:

- `KV { key, value, hidden_if_empty }`
- `#[serde(default, alias = "hiddenIfEmpty")]` on `KV::hidden_if_empty`
- `KVS(pub Vec<KV>)`
- `Config(pub HashMap<String, HashMap<String, KVS>>)`
- `KVS::new`, `get`, `lookup`, `is_empty`, `keys`, `insert`, and `extend`
- `Config::new`, `get_value`, `set_defaults`, `marshal`, `unmarshal`, and
  `merge`
- `Config::new()` default application after `ecstore::config::init()`
- existing persisted server-config JSON shape
- existing target, notify, audit, scanner, OIDC, and admin interpretation of
  `Config` and `KVS`

## Next PR Requirements

`CFG-003` should be a pure model extraction or narrow `api-extraction` PR. It
must not migrate consumers, change persistence helpers, or alter runtime
behavior.

`CFG-004` kept the old `rustfs_ecstore::config::*` path as a temporary
compatibility shim, registered its removal condition, and removed the shim after
all in-repo consumers migrated.

`CFG-005` should migrate external consumers one group at a time after the model
and compatibility path are stable.

`CFG-008` moves only the global server-config snapshot accessors to
`rustfs-config` and migrates in-repo direct consumers. It must not move
`ConfigSys`, storage-class global state, persistence helpers, default
registration wiring, startup order, or storage behavior.

## Verification Gate

Before pushing an extraction PR, run:

- serde roundtrip tests for old and new paths
- tests for `hiddenIfEmpty` alias compatibility
- tests for `KVS` insertion, lookup, extension, and keys behavior
- tests for `Config::new`, `set_defaults`, `marshal`, `unmarshal`, and `merge`
- a cleanup scan proving in-repo consumers no longer use the old
  `rustfs_ecstore::config::{Config, KV, KVS}` model path before removing the
  compatibility shim
- `cargo tree -p rustfs-config --edges normal`
- `cargo tree -p rustfs-ecstore --edges normal`
- `./scripts/check_layer_dependencies.sh`
- `./scripts/check_architecture_migration_rules.sh`
- `cargo fmt --all --check`
- `make pre-commit`

## Non-Goals

- No consumer migration in `CFG-002`.
- No code movement in `CFG-002`.
- No new crate in `CFG-002`.
- No `com.rs` or `StorageAPI` movement in the first model extraction.
- No global server-config state migration until the model path is stable.
