This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal-reqsign.git


The following commit(s) were added to refs/heads/main by this push:
     new c5400d8  docs: define default credential provider API policy (#708)
c5400d8 is described below

commit c5400d8d2cb3a309f2171f3ec24ac0c066fdfcd5
Author: Xuanwo <[email protected]>
AuthorDate: Wed Mar 18 18:39:32 2026 +0800

    docs: define default credential provider API policy (#708)
    
    This PR turns the default credential provider API shape into explicit
    repository documentation instead of leaving it implicit in legacy
    implementations. It records the authoritative design in
    `docs/default-credential-provider-api.md` and keeps `AGENTS.md` focused
    on durable agent-facing constraints.
    
    It also converts `CLAUDE.md` into an alias of `AGENTS.md` so repository
    guidance has a single source of truth before we start the API refactor
    itself.
---
 AGENTS.md                               |  80 +++++++++++++
 CLAUDE.md                               |  83 +-------------
 docs/default-credential-provider-api.md | 197 ++++++++++++++++++++++++++++++++
 3 files changed, 278 insertions(+), 82 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..cd4dc01
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,80 @@
+# CLAUDE.md
+
+## Project Overview
+
+Apache OpenDAL reqsign — a Rust library for signing HTTP API requests across
+cloud providers (AWS, Azure, Google, Aliyun, Huawei, Tencent, Oracle,
+Volcengine).
+
+## Architecture
+
+The codebase is a Cargo workspace with three layers:
+
+- **`core`** (`reqsign-core`): traits (`ProvideCredential`, `SignRequest`,
+  `SigningCredential`) and the runtime-agnostic `Context` / `Signer` types.
+- **`context/*`**: pluggable runtime adapters (`file-read-tokio`,
+  `http-send-reqwest`, `command-execute-tokio`).
+- **`services/*`**: per-cloud signing implementations, each its own crate.
+- **`reqsign`**: facade crate re-exporting everything behind feature flags;
+  provides `default_context()` and per-service `default_signer()`.
+
+### Key Design Decisions
+
+- **No default implementations in `Context`**: `Context::new()` wires up
+  no-op stubs. Users (or `default_context()`) must explicitly plug in
+  `FileRead`, `HttpSend`, `Env`, `CommandExecute`.
+- **`MaybeSend` futures**: core traits use `MaybeSend` instead of `async_trait`
+  so that the crate compiles for `wasm32-unknown-unknown`.
+- **Dyn-trait pairs**: every async trait `Foo` has a `FooDyn` counterpart and a
+  blanket `impl FooDyn for T: Foo`, enabling `Arc<dyn FooDyn>` inside `Signer`.
+
+## API Design References
+
+When working on an API area with an explicit design document, follow that
+document as the source of truth instead of inferring policy from the current
+implementation.
+
+### Default Credential Provider
+
+All future refactors and API reviews around `DefaultCredentialProvider` must
+follow the authoritative design in
+[docs/default-credential-provider-api.md](docs/default-credential-provider-api.md).
+
+Treat any future API change that reintroduces the following as a design
+regression unless explicitly approved: `configure_*`, `disable_*(bool)`,
+fallback-based re-enabling of removed slots during `build()`.
+
+## Build & Test
+
+```bash
+cargo fmt --all -- --check
+cargo clippy --workspace --all-targets --all-features -- -D warnings
+cargo test --no-fail-fast            # unit tests
+cargo test --doc --all-features --workspace  # doc tests
+```
+
+### Integration Tests
+
+Integration tests live per-service under `services/<name>/tests/` and are gated
+by `REQSIGN_<SERVICE>_TEST*` env vars. Secrets are loaded via 1Password Connect
+in CI. Some providers (IMDS, ECS, SSO, Process, Cognito) use Python mock
+servers checked into `services/aws-v4/tests/mocks/`.
+
+## WASM Compatibility
+
+`reqsign-core` and a subset of services (`aws`, `azure`, `aliyun`, `tencent`)
+must compile for `wasm32-unknown-unknown`. Google is excluded because
+`jsonwebtoken` does not support WASM. CI verifies this.
+
+## Versioning
+
+- Service crates and core share a lockstep major version (`3.0.0`).
+- `reqsign-http-send-reqwest` has its own major (`4.0.0`).
+- The facade `reqsign` crate follows an independent scheme (`0.20.0`).
+- Release is triggered by pushing a `v*.*.*` tag; `cargo publish --workspace`
+  publishes everything.
+
+## License
+
+Apache-2.0 (ASF). All source files must carry the Apache header, enforced by
+hawkeye via `licenserc.toml`.
diff --git a/CLAUDE.md b/CLAUDE.md
deleted file mode 100644
index 8d18801..0000000
--- a/CLAUDE.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# CLAUDE.md
-
-This file provides guidance to Claude Code (claude.ai/code) when working with 
code in this repository.
-
-## Project Overview
-
-reqsign is a Rust library for signing HTTP API requests for cloud services 
(AWS, Azure, Google Cloud, Aliyun, Huawei Cloud, Oracle Cloud, Tencent Cloud). 
It follows a "build, sign, send" philosophy with modular architecture.
-
-## Common Development Commands
-
-### Build and Check
-```bash
-cargo check                                    # Analyze code without building
-cargo build --workspace                        # Build all crates
-cargo build --workspace --release              # Build optimized release 
version
-```
-
-### Testing
-```bash
-cargo test --no-fail-fast                      # Run all tests
-cargo test --workspace --no-fail-fast          # Test entire workspace
-cargo test tests::it::services::fs             # Test specific function
-cargo test -p reqsign-aws-v4                   # Test specific service crate
-RUST_LOG=debug cargo test                      # Test with debug logging
-```
-
-### Linting and Formatting
-```bash
-cargo fmt --all                                # Format all code
-cargo fmt --all -- --check                     # Check formatting without 
changes
-cargo clippy --workspace --all-targets --all-features -- -D warnings  # Lint 
with all features
-```
-
-### WASM Build
-```bash
-cargo build --workspace --target wasm32-unknown-unknown --exclude 
reqsign-file-read-tokio --exclude reqsign-http-send-reqwest
-```
-
-## Architecture
-
-### Workspace Structure
-- `core/` - Core signing functionality and abstractions
-- `context/` - Pluggable I/O implementations:
-  - `file-read-tokio/` - Async file reading with Tokio
-  - `http-send-reqwest/` - HTTP client with reqwest
-- `services/` - Provider-specific implementations:
-  - `aws-v4/` - AWS Signature Version 4
-  - `azure-storage/` - Azure Storage services
-  - `google/` - Google Cloud services
-  - `aliyun-oss/`, `huaweicloud-obs/`, `oracle/`, `tencent-cos/` - Other 
providers
-- `reqsign/` - Main crate that re-exports all functionality with feature flags
-
-### Key Design Patterns
-1. **Context System**: Abstract I/O operations (file reading, HTTP sending) 
behind traits in `core`, with implementations in `context/`
-2. **Service Modularity**: Each cloud provider is a separate crate, allowing 
users to include only needed providers
-3. **Feature Flags**: Main `reqsign` crate uses features to control which 
services are included
-4. **Credential Loading**: Services load credentials from environment 
variables, with provider-specific prefixes
-
-### Testing Strategy
-- Copy `.env.example` to `.env` and configure service credentials for 
integration tests
-- Service tests can be disabled via environment variables (e.g., 
`REQSIGN_AWS_V4_TEST=false`)
-- Tests require real service credentials for full integration testing
-- Use `RUST_LOG=debug` for detailed test output
-
-## Development Tips
-
-### Adding New Services
-1. Create new crate in `services/` directory
-2. Implement service-specific signing logic
-3. Add feature flag in main `reqsign/Cargo.toml`
-4. Re-export in `reqsign/src/lib.rs` behind feature gate
-
-### Working with Specific Services
-```bash
-cd services/aws-v4 && cargo test              # Test single service
-cargo test -p reqsign-azure-storage           # Test from workspace root
-```
-
-### Debugging
-- Set `RUST_LOG=debug` for verbose output
-- Set `RUST_BACKTRACE=full` for detailed error traces
-- Check `.env.example` for required environment variables per service
\ No newline at end of file
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 120000
index 0000000..47dc3e3
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+AGENTS.md
\ No newline at end of file
diff --git a/docs/default-credential-provider-api.md 
b/docs/default-credential-provider-api.md
new file mode 100644
index 0000000..d46ee92
--- /dev/null
+++ b/docs/default-credential-provider-api.md
@@ -0,0 +1,197 @@
+# Default Credential Provider API Design
+
+This document defines the repository's authoritative public API design for all
+`DefaultCredentialProvider` types.
+
+Implementations that do not match this document should be treated as legacy
+debt. New code, reviews, and refactors must follow this document.
+
+## Goals
+
+- Make provider participation explicit.
+- Remove boolean toggles from the public API.
+- Remove `configure_*` patch-style APIs from the public API.
+- Keep the top-level `DefaultCredentialProvider` API uniform across services.
+- Keep builder APIs predictable across services while allowing service-specific
+  provider slots.
+
+## Product API
+
+Every service-level default provider exposes the same product-level API:
+
+```rust
+DefaultCredentialProvider::new()
+DefaultCredentialProvider::builder()
+DefaultCredentialProvider::with_chain(chain)
+DefaultCredentialProvider::push_front(provider)
+impl Default for DefaultCredentialProvider
+```
+
+### Semantics
+
+- `new()` creates the documented default chain for that service.
+- `default()` is identical to `new()`.
+- `builder()` returns a builder pre-populated with the documented default 
slots.
+- `with_chain(chain)` bypasses all default-chain assembly logic.
+- `push_front(provider)` prepends a high-priority provider in front of the
+  documented default chain.
+
+## Builder API
+
+Every `DefaultCredentialProviderBuilder` exposes:
+
+```rust
+DefaultCredentialProviderBuilder::new()
+DefaultCredentialProviderBuilder::default()
+.build()
+```
+
+For each supported provider slot, the builder exposes exactly two primary
+methods:
+
+```rust
+.env(provider)
+.no_env()
+```
+
+The same pattern applies to all other slots:
+
+```rust
+.profile(provider)
+.no_profile()
+
+.sso(provider)
+.no_sso()
+
+.imds(provider)
+.no_imds()
+```
+
+### Rules
+
+- Public builder APIs must not expose `configure_*`.
+- Public builder APIs must not expose `disable_*(bool)`.
+- Each slot is controlled by one positive method and one removal method.
+- Slot names must match the actual provider concept, not an overloaded alias.
+- Builders expose only the slots that a service actually supports.
+
+## Internal State Model
+
+Each builder slot should be represented as:
+
+```rust
+Option<T>
+```
+
+with this meaning:
+
+- `Some(T::default())`: slot enabled with the default provider configuration
+- `Some(custom)`: slot enabled with a custom provider configuration
+- `None`: slot removed from the chain
+
+This implies:
+
+- `DefaultCredentialProviderBuilder::default()` initializes all documented
+  default slots to `Some(T::default())`.
+- `.no_env()` sets `env` to `None`.
+- `.env(provider)` sets `env` to `Some(provider)`.
+- `build()` pushes only `Some(...)` slots and skips `None`.
+
+No separate `enabled` flag is needed.
+
+## Slot Naming
+
+Use provider-concept names without the `CredentialProvider` suffix.
+
+Examples:
+
+- `env`
+- `profile`
+- `sso`
+- `process`
+- `ecs`
+- `imds`
+- `web_identity`
+- `oidc`
+- `config_file`
+- `vm_metadata`
+
+Do not collapse distinct concepts under a vague shared name.
+
+Examples:
+
+- Prefer `web_identity(...)` over `assume_role(...)` when the slot is actually
+  `AssumeRoleWithWebIdentityCredentialProvider`.
+- Prefer `oidc(...)` over `assume_role(...)` when the slot is specifically
+  OIDC-based.
+
+## Service Examples
+
+### AWS V4
+
+```rust
+let provider = DefaultCredentialProvider::builder()
+    .no_env()
+    .profile(ProfileCredentialProvider::new().with_profile("prod"))
+    .no_imds()
+    .build();
+```
+
+Expected slots:
+
+- `env`
+- `profile`
+- `sso`
+- `web_identity`
+- `process`
+- `ecs`
+- `imds`
+
+### Aliyun OSS
+
+```rust
+let provider = DefaultCredentialProvider::builder()
+    .env(EnvCredentialProvider::new())
+    .no_oidc()
+    .build();
+```
+
+Expected slots:
+
+- `env`
+- `oidc`
+
+### Tencent COS
+
+```rust
+let provider = DefaultCredentialProvider::builder()
+    .no_env()
+    .web_identity(AssumeRoleWithWebIdentityCredentialProvider::new())
+    .build();
+```
+
+Expected slots:
+
+- `env`
+- `web_identity`
+
+## Migration Rules
+
+When changing an existing service:
+
+1. Remove all public `configure_*` methods.
+2. Remove all public `disable_*(bool)` methods.
+3. Initialize builder defaults with `Some(T::default())` for documented default
+   slots.
+4. Add `slot(provider)` and `no_slot()` methods for every supported slot.
+5. Update tests to verify that `no_slot()` truly removes the provider from the
+   chain.
+6. Update examples and docs to use slot/no-slot APIs only.
+
+## Non-Goals
+
+- This document does not require a repository-wide refactor in one patch.
+- This document does not require every service to expose the same set of
+  provider slots.
+- This document does not remove `with_chain` or `push_front`; those remain the
+  escape hatch for advanced composition.

Reply via email to